adhearsion 1.2.6 → 2.0.0.alpha1

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.
Files changed (236) hide show
  1. data/.gitignore +17 -10
  2. data/CHANGELOG.md +273 -0
  3. data/Gemfile +1 -1
  4. data/Guardfile +17 -0
  5. data/README.markdown +61 -9
  6. data/Rakefile +16 -48
  7. data/adhearsion.gemspec +21 -7
  8. data/bin/ahn +3 -1
  9. data/cucumber.yml +4 -0
  10. data/features/app_generator.feature +42 -0
  11. data/features/cli.feature +108 -0
  12. data/features/step_definitions/app_generator_steps.rb +6 -0
  13. data/features/step_definitions/cli_steps.rb +74 -0
  14. data/features/support/aruba_helper.rb +22 -0
  15. data/features/support/env.rb +37 -0
  16. data/features/support/utils.rb +8 -0
  17. data/lib/adhearsion.rb +85 -41
  18. data/lib/adhearsion/call.rb +176 -0
  19. data/lib/adhearsion/call_controller.rb +134 -0
  20. data/lib/adhearsion/call_controller/dial.rb +70 -0
  21. data/lib/adhearsion/call_controller/input.rb +173 -0
  22. data/lib/adhearsion/call_controller/menu.rb +124 -0
  23. data/lib/adhearsion/call_controller/output.rb +267 -0
  24. data/lib/adhearsion/call_controller/record.rb +42 -0
  25. data/lib/adhearsion/call_controller/utility.rb +60 -0
  26. data/lib/adhearsion/calls.rb +81 -0
  27. data/lib/adhearsion/cli.rb +1 -3
  28. data/lib/adhearsion/cli_commands.rb +142 -0
  29. data/lib/adhearsion/configuration.rb +149 -0
  30. data/lib/adhearsion/console.rb +19 -8
  31. data/lib/adhearsion/dialplan_controller.rb +9 -0
  32. data/lib/adhearsion/events.rb +84 -0
  33. data/lib/adhearsion/foundation/all.rb +0 -7
  34. data/lib/adhearsion/foundation/custom_daemonizer.rb +4 -6
  35. data/lib/adhearsion/foundation/exception_handler.rb +9 -0
  36. data/lib/adhearsion/foundation/object.rb +26 -8
  37. data/lib/adhearsion/foundation/synchronized_hash.rb +3 -6
  38. data/lib/adhearsion/foundation/thread_safety.rb +17 -1
  39. data/lib/adhearsion/generators/app/app_generator.rb +4 -13
  40. data/lib/adhearsion/generators/app/templates/Gemfile +10 -5
  41. data/lib/adhearsion/generators/app/templates/Procfile +1 -0
  42. data/lib/adhearsion/generators/app/templates/README.md +28 -0
  43. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +41 -0
  44. data/lib/adhearsion/generators/app/templates/{components/simon_game → lib}/simon_game.rb +6 -18
  45. data/lib/adhearsion/generators/app/templates/script/ahn +2 -2
  46. data/lib/adhearsion/initializer.rb +151 -293
  47. data/lib/adhearsion/initializer/logging.rb +33 -0
  48. data/lib/adhearsion/logging.rb +65 -69
  49. data/lib/adhearsion/menu_dsl.rb +15 -0
  50. data/lib/adhearsion/menu_dsl/calculated_match.rb +39 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +41 -0
  52. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +18 -0
  53. data/lib/adhearsion/menu_dsl/match_calculator.rb +36 -0
  54. data/lib/adhearsion/{voip/menu_state_machine/menu_class.rb → menu_dsl/menu.rb} +38 -40
  55. data/lib/adhearsion/menu_dsl/menu_builder.rb +69 -0
  56. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +55 -0
  57. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +21 -0
  58. data/lib/adhearsion/outbound_call.rb +64 -0
  59. data/lib/adhearsion/plugin.rb +319 -0
  60. data/lib/adhearsion/plugin/collection.rb +19 -0
  61. data/lib/adhearsion/plugin/initializer.rb +37 -0
  62. data/lib/adhearsion/plugin/methods_container.rb +6 -0
  63. data/lib/adhearsion/process.rb +94 -0
  64. data/lib/adhearsion/punchblock_plugin.rb +29 -0
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +137 -0
  66. data/lib/adhearsion/router.rb +30 -0
  67. data/lib/adhearsion/router/route.rb +42 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -2
  69. data/lib/adhearsion/tasks.rb +14 -9
  70. data/lib/adhearsion/tasks/configuration.rb +26 -0
  71. data/lib/adhearsion/tasks/plugins.rb +17 -0
  72. data/lib/adhearsion/version.rb +8 -14
  73. data/spec/adhearsion/call_controller/dial_spec.rb +138 -0
  74. data/spec/adhearsion/call_controller/input_spec.rb +278 -0
  75. data/spec/adhearsion/call_controller/menu_spec.rb +120 -0
  76. data/spec/adhearsion/call_controller/output_spec.rb +466 -0
  77. data/spec/adhearsion/call_controller/record_spec.rb +125 -0
  78. data/spec/adhearsion/call_controller_spec.rb +395 -0
  79. data/spec/adhearsion/call_spec.rb +438 -0
  80. data/spec/adhearsion/calls_spec.rb +47 -0
  81. data/spec/adhearsion/configuration_spec.rb +308 -0
  82. data/spec/adhearsion/dialplan_controller_spec.rb +26 -0
  83. data/spec/adhearsion/events_spec.rb +112 -0
  84. data/spec/adhearsion/initializer/logging_spec.rb +58 -0
  85. data/spec/adhearsion/initializer_spec.rb +209 -122
  86. data/spec/adhearsion/logging_spec.rb +58 -47
  87. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +56 -0
  88. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +57 -0
  89. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +33 -0
  90. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +13 -0
  91. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +118 -0
  92. data/spec/adhearsion/menu_dsl/menu_spec.rb +210 -0
  93. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +28 -0
  94. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +36 -0
  95. data/spec/adhearsion/menu_dsl_spec.rb +12 -0
  96. data/spec/adhearsion/outbound_call_spec.rb +174 -0
  97. data/spec/adhearsion/plugin_spec.rb +489 -0
  98. data/spec/adhearsion/process_spec.rb +34 -0
  99. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +294 -0
  100. data/spec/adhearsion/router/route_spec.rb +99 -0
  101. data/spec/adhearsion/router_spec.rb +106 -0
  102. data/spec/adhearsion_spec.rb +46 -0
  103. data/spec/spec_helper.rb +14 -14
  104. data/spec/support/call_controller_test_helpers.rb +48 -0
  105. data/spec/support/initializer_stubs.rb +8 -13
  106. data/spec/support/punchblock_mocks.rb +6 -0
  107. metadata +255 -253
  108. data/CHANGELOG +0 -174
  109. data/bin/ahnctl +0 -68
  110. data/bin/jahn +0 -43
  111. data/examples/asterisk_manager_interface/standalone.rb +0 -51
  112. data/lib/adhearsion/commands.rb +0 -302
  113. data/lib/adhearsion/component_manager.rb +0 -278
  114. data/lib/adhearsion/component_manager/component_tester.rb +0 -54
  115. data/lib/adhearsion/component_manager/spec_framework.rb +0 -18
  116. data/lib/adhearsion/events_support.rb +0 -65
  117. data/lib/adhearsion/foundation/blank_slate.rb +0 -3
  118. data/lib/adhearsion/foundation/event_socket.rb +0 -205
  119. data/lib/adhearsion/foundation/future_resource.rb +0 -36
  120. data/lib/adhearsion/foundation/metaprogramming.rb +0 -17
  121. data/lib/adhearsion/foundation/numeric.rb +0 -13
  122. data/lib/adhearsion/foundation/pseudo_guid.rb +0 -10
  123. data/lib/adhearsion/foundation/relationship_properties.rb +0 -42
  124. data/lib/adhearsion/foundation/string.rb +0 -26
  125. data/lib/adhearsion/generators/app/templates/.ahnrc +0 -34
  126. data/lib/adhearsion/generators/app/templates/README +0 -8
  127. data/lib/adhearsion/generators/app/templates/components/ami_remote/ami_remote.rb +0 -15
  128. data/lib/adhearsion/generators/app/templates/components/disabled/HOW_TO_ENABLE +0 -7
  129. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/README.markdown +0 -47
  130. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.rb +0 -34
  131. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.yml +0 -12
  132. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/README.markdown +0 -3
  133. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +0 -11
  134. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  135. data/lib/adhearsion/generators/app/templates/config/startup.rb +0 -81
  136. data/lib/adhearsion/generators/app/templates/dialplan.rb +0 -3
  137. data/lib/adhearsion/generators/app/templates/events.rb +0 -33
  138. data/lib/adhearsion/host_definitions.rb +0 -67
  139. data/lib/adhearsion/initializer/asterisk.rb +0 -86
  140. data/lib/adhearsion/initializer/configuration.rb +0 -324
  141. data/lib/adhearsion/initializer/database.rb +0 -60
  142. data/lib/adhearsion/initializer/drb.rb +0 -31
  143. data/lib/adhearsion/initializer/freeswitch.rb +0 -22
  144. data/lib/adhearsion/initializer/ldap.rb +0 -57
  145. data/lib/adhearsion/initializer/rails.rb +0 -41
  146. data/lib/adhearsion/initializer/xmpp.rb +0 -42
  147. data/lib/adhearsion/tasks/components.rb +0 -32
  148. data/lib/adhearsion/tasks/database.rb +0 -5
  149. data/lib/adhearsion/tasks/deprecations.rb +0 -59
  150. data/lib/adhearsion/tasks/generating.rb +0 -20
  151. data/lib/adhearsion/tasks/lint.rb +0 -4
  152. data/lib/adhearsion/voip/asterisk.rb +0 -4
  153. data/lib/adhearsion/voip/asterisk/agi_server.rb +0 -121
  154. data/lib/adhearsion/voip/asterisk/commands.rb +0 -1966
  155. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +0 -140
  156. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +0 -102
  157. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +0 -250
  158. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +0 -240
  159. data/lib/adhearsion/voip/asterisk/config_manager.rb +0 -64
  160. data/lib/adhearsion/voip/asterisk/manager_interface.rb +0 -697
  161. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +0 -1681
  162. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +0 -341
  163. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +0 -78
  164. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +0 -87
  165. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +0 -80
  166. data/lib/adhearsion/voip/call.rb +0 -521
  167. data/lib/adhearsion/voip/call_routing.rb +0 -64
  168. data/lib/adhearsion/voip/commands.rb +0 -17
  169. data/lib/adhearsion/voip/constants.rb +0 -39
  170. data/lib/adhearsion/voip/conveniences.rb +0 -18
  171. data/lib/adhearsion/voip/dial_plan.rb +0 -252
  172. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +0 -151
  173. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +0 -37
  174. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +0 -27
  175. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +0 -124
  176. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +0 -69
  177. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +0 -16
  178. data/lib/adhearsion/voip/dsl/numerical_string.rb +0 -128
  179. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +0 -48
  180. data/lib/adhearsion/voip/freeswitch/event_handler.rb +0 -58
  181. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +0 -129
  182. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +0 -38
  183. data/lib/adhearsion/voip/freeswitch/oes_server.rb +0 -195
  184. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +0 -80
  185. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +0 -123
  186. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +0 -57
  187. data/lib/adhearsion/xmpp/connection.rb +0 -61
  188. data/lib/theatre.rb +0 -147
  189. data/lib/theatre/README.markdown +0 -64
  190. data/lib/theatre/callback_definition_loader.rb +0 -86
  191. data/lib/theatre/guid.rb +0 -23
  192. data/lib/theatre/invocation.rb +0 -131
  193. data/lib/theatre/namespace_manager.rb +0 -153
  194. data/lib/theatre/version.rb +0 -2
  195. data/spec/adhearsion/cli_spec.rb +0 -306
  196. data/spec/adhearsion/component_manager_spec.rb +0 -292
  197. data/spec/adhearsion/constants_spec.rb +0 -8
  198. data/spec/adhearsion/drb_spec.rb +0 -65
  199. data/spec/adhearsion/fixtures/dialplan.rb +0 -3
  200. data/spec/adhearsion/foundation/event_socket_spec.rb +0 -168
  201. data/spec/adhearsion/host_definitions_spec.rb +0 -79
  202. data/spec/adhearsion/initializer/configuration_spec.rb +0 -291
  203. data/spec/adhearsion/initializer/loading_spec.rb +0 -154
  204. data/spec/adhearsion/initializer/paths_spec.rb +0 -74
  205. data/spec/adhearsion/relationship_properties_spec.rb +0 -54
  206. data/spec/adhearsion/voip/asterisk/agi_server_spec.rb +0 -473
  207. data/spec/adhearsion/voip/asterisk/ami/ami_spec.rb +0 -550
  208. data/spec/adhearsion/voip/asterisk/ami/lexer/ami_fixtures.yml +0 -30
  209. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story +0 -291
  210. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story.rb +0 -241
  211. data/spec/adhearsion/voip/asterisk/ami/lexer/story_helper.rb +0 -124
  212. data/spec/adhearsion/voip/asterisk/commands_spec.rb +0 -3241
  213. data/spec/adhearsion/voip/asterisk/config_file_generators/agents_spec.rb +0 -251
  214. data/spec/adhearsion/voip/asterisk/config_file_generators/queues_spec.rb +0 -323
  215. data/spec/adhearsion/voip/asterisk/config_file_generators/voicemail_spec.rb +0 -306
  216. data/spec/adhearsion/voip/asterisk/config_manager_spec.rb +0 -127
  217. data/spec/adhearsion/voip/asterisk/menu_command/calculated_match_spec.rb +0 -109
  218. data/spec/adhearsion/voip/asterisk/menu_command/matchers_spec.rb +0 -97
  219. data/spec/adhearsion/voip/call_routing_spec.rb +0 -125
  220. data/spec/adhearsion/voip/dialplan_manager_spec.rb +0 -468
  221. data/spec/adhearsion/voip/dsl/dialing_dsl_spec.rb +0 -270
  222. data/spec/adhearsion/voip/dsl/dispatcher_spec.rb +0 -82
  223. data/spec/adhearsion/voip/dsl/dispatcher_spec_helper.rb +0 -45
  224. data/spec/adhearsion/voip/dsl/parser_spec.rb +0 -69
  225. data/spec/adhearsion/voip/freeswitch/basic_connection_manager_spec.rb +0 -39
  226. data/spec/adhearsion/voip/freeswitch/inbound_connection_manager_spec.rb +0 -39
  227. data/spec/adhearsion/voip/freeswitch/oes_server_spec.rb +0 -9
  228. data/spec/adhearsion/voip/numerical_string_spec.rb +0 -61
  229. data/spec/adhearsion/voip/phone_number_spec.rb +0 -45
  230. data/spec/support/the_following_code.rb +0 -3
  231. data/spec/theatre/dsl_examples/simple_before_call.rb +0 -7
  232. data/spec/theatre/dsl_spec.rb +0 -69
  233. data/spec/theatre/invocation_spec.rb +0 -182
  234. data/spec/theatre/namespace_spec.rb +0 -125
  235. data/spec/theatre/spec_helper_spec.rb +0 -28
  236. data/spec/theatre/theatre_class_spec.rb +0 -148
@@ -0,0 +1,69 @@
1
+ module Adhearsion
2
+ module MenuDSL
3
+
4
+ class MenuBuilder
5
+
6
+ attr_accessor :patterns, :menu_callbacks
7
+
8
+ def initialize
9
+ @patterns = []
10
+ @menu_callbacks = {}
11
+ end
12
+
13
+ def build(&block)
14
+ @context = eval "self", block.binding
15
+ instance_eval &block
16
+ end
17
+
18
+ def match(*args, &block)
19
+ payload = if block_given?
20
+ raise ArgumentError, "You cannot specify both a block and a controller name." if args.last.is_a? Class
21
+ nil
22
+ else
23
+ raise ArgumentError, "You need to provide a block or a controller name." unless args.last.is_a? Class
24
+ args.pop
25
+ end
26
+
27
+ raise ArgumentError, "You cannot call this method without patterns." if args.empty?
28
+
29
+ args.each do |pattern|
30
+ @patterns << MatchCalculator.build_with_pattern(pattern, payload, &block)
31
+ end
32
+ end
33
+
34
+ def weighted_match_calculators
35
+ @patterns
36
+ end
37
+
38
+ def execute_hook_for(symbol, input)
39
+ callback = @menu_callbacks[symbol]
40
+ @context.instance_exec input, &callback
41
+ end
42
+
43
+ def invalid(&block)
44
+ raise LocalJumpError, "Must supply a block!" unless block_given?
45
+ @menu_callbacks[:invalid] = block
46
+ end
47
+
48
+ def timeout(&block)
49
+ raise LocalJumpError, "Must supply a block!" unless block_given?
50
+ @menu_callbacks[:timeout] = block
51
+ end
52
+
53
+ def failure(&block)
54
+ raise LocalJumpError, "Must supply a block!" unless block_given?
55
+ @menu_callbacks[:failure] = block
56
+ end
57
+
58
+ def calculate_matches_for(result)
59
+ CalculatedMatchCollection.new.tap do |collection|
60
+ weighted_match_calculators.each do |pattern|
61
+ collection << pattern.match(result)
62
+ end
63
+ end
64
+ end
65
+
66
+ end # class MenuBuilder
67
+
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ module Adhearsion
2
+ module MenuDSL
3
+
4
+ class RangeMatchCalculator < MatchCalculator
5
+
6
+ def initialize(pattern, match_payload)
7
+ raise unless pattern.first.kind_of?(Numeric) && pattern.last.kind_of?(Numeric)
8
+ super
9
+ end
10
+
11
+ def match(query)
12
+ numerical_query = coerce_to_numeric query
13
+ if numerical_query
14
+ exact_match = pattern.include?(numerical_query) ? query : nil
15
+ potential_matches = numbers_in_range_like numerical_query
16
+ potential_matches.reject! { |m| m.to_s == exact_match.to_s } if exact_match
17
+
18
+ new_calculated_match :query => query, :exact_matches => exact_match,
19
+ :potential_matches => potential_matches
20
+ else
21
+ CalculatedMatch.failed_match! pattern, query, match_payload
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # Returns all numbers in the range (@pattern) that +begin with+ the number given
28
+ # as the first arguement.
29
+ #
30
+ # NOTE: If you're having trouble reading what this method is actually doing. It's
31
+ # effectively a much more efficient version of this:
32
+ #
33
+ # pattern.to_a.select { |x| x.to_s.starts_with? num.to_s }.flatten
34
+ #
35
+ # Huge thanks to Dave Troy (http://davetroy.blogspot.com) for this awesomely
36
+ # efficient code!
37
+ def numbers_in_range_like(num)
38
+ return (pattern === 0 ? [0] : nil) if num == 0
39
+ raise ArgumentError unless num.kind_of?(Numeric)
40
+ Array.new.tap do |matches|
41
+ first, last = pattern.first, pattern.last
42
+ power = 0
43
+ while num < last
44
+ ones_count = 10**power - 1
45
+ matches.concat ([num, first].max..[num + ones_count, last].min).to_a
46
+ num *= 10
47
+ power += 1
48
+ end
49
+ end
50
+ end
51
+
52
+ end # class RangeMatchCalculator
53
+
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Adhearsion
2
+ module MenuDSL
3
+
4
+ class StringMatchCalculator < MatchCalculator
5
+
6
+ def match(query)
7
+ args = { :query => query, :exact_matches => nil, :potential_matches => nil }
8
+
9
+ if pattern == query.to_s
10
+ args[:exact_matches] = [pattern]
11
+ elsif pattern.starts_with? query.to_s
12
+ args[:potential_matches] = [pattern]
13
+ end
14
+
15
+ new_calculated_match args
16
+ end
17
+
18
+ end # class StringMatchCalculator
19
+
20
+ end
21
+ end
@@ -0,0 +1,64 @@
1
+ module Adhearsion
2
+ class OutboundCall < Call
3
+ attr_reader :dial_command
4
+
5
+ class << self
6
+ def originate(to, opts = {})
7
+ new.tap do |call|
8
+ call.run_router_on_answer
9
+ call.dial to, opts
10
+ end
11
+ end
12
+ end
13
+
14
+ def id
15
+ dial_command.call_id if dial_command
16
+ end
17
+
18
+ def variables
19
+ {}
20
+ end
21
+
22
+ def client
23
+ PunchblockPlugin::Initializer.client
24
+ end
25
+
26
+ def accept(*args)
27
+ end
28
+
29
+ def answer(*args)
30
+ end
31
+
32
+ def reject(*args)
33
+ end
34
+
35
+ def dial(to, options = {})
36
+ options.merge! :to => to
37
+ write_and_await_response(Punchblock::Command::Dial.new(options)).tap do |dial_command|
38
+ @dial_command = dial_command
39
+ Adhearsion.active_calls << self
40
+ end
41
+ end
42
+
43
+ def run_router
44
+ catching_standard_errors do
45
+ dispatcher = Adhearsion.router.handle self
46
+ dispatcher.call self
47
+ end
48
+ end
49
+
50
+ def run_router_on_answer
51
+ register_event_handler :class => Punchblock::Event::Answered do |event|
52
+ run_router
53
+ throw :pass
54
+ end
55
+ end
56
+
57
+ def on_answer(&block)
58
+ register_event_handler :class => Punchblock::Event::Answered do |event|
59
+ block.call event
60
+ throw :pass
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,319 @@
1
+ module Adhearsion
2
+
3
+ # Plugin is the core of extension of Adhearsion framework and provides the easiest
4
+ # path to add new functionality, configuration or modify the initialization process.
5
+ #
6
+ # Its behavior is based on Rails::Railtie, so if you are familiar with Rails
7
+ # this will be easier for you to start using Adhearsion::Plugin, but of course
8
+ # no previous knowledge is required.
9
+ #
10
+ # With an Adhearsion Plugin you can:
11
+ #
12
+ # * create initializers
13
+ # * add rake tasks to Adhearsion
14
+ # * add/modify configuration files
15
+ # * add dialplan, rpc, console and events methods
16
+ #
17
+ # == How to create your Adhearsion Plugin
18
+ #
19
+ # Create a class that inherits from Adhearsion::Plugin within your plugin namespace.
20
+ # This class shall be loaded during your awesome Adhearsion application boot process.
21
+ #
22
+ # # lib/my_plugin/plugin.rb
23
+ # module MyPlugin
24
+ # class Plugin < Adhearsion::Plugin
25
+ # end
26
+ # end
27
+ #
28
+ # == How to add a new dialplan method
29
+ #
30
+ # module MyPlugin
31
+ # class Plugin < Adhearsion::Plugin
32
+ # dialplan :my_new_dialplan_method do
33
+ # logger.info "this dialplan method is really awesome #{call.inspect}. It says 'hello world'"
34
+ # speak "hello world"
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # Create a new rpc, console or events methods is as ease just following this approach
40
+ #
41
+ # == Execute a specific code while initializing Adhearison
42
+ #
43
+ # module MyPlugin
44
+ # class Plugin < Adhearsion::Plugin
45
+ # init :my_plugin do
46
+ # logger.warn "I want to ensure my plugin is being loaded!!!"
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # As Rails::Railtie does, you can define the exact point when you want to load your plugin
52
+ # during the initilization process
53
+ #
54
+ # module MyPlugin
55
+ # class Plugin < Adhearsion::Plugin
56
+ # init :my_plugin, :after => :my_other_plugin do
57
+ # logger.warn "My Plugin depends on My Other Plugin, so it must be loaded after"
58
+ # end
59
+ # end
60
+ # end
61
+ #
62
+ class Plugin
63
+
64
+ extend ActiveSupport::Autoload
65
+
66
+ METHODS_OPTIONS = {:load => true, :scope => false}
67
+
68
+ SCOPE_NAMES = [:dialplan, :rpc, :events, :console]
69
+
70
+ autoload :Configuration
71
+ autoload :Collection
72
+ autoload :Initializer
73
+ autoload :MethodsContainer
74
+
75
+ class << self
76
+ # Metaprogramming to create the class methods that can be used in user defined plugins to
77
+ # create specific scope methods
78
+ SCOPE_NAMES.each do |name|
79
+
80
+ # This block will create the relevant methods to handle how to add new methods
81
+ # to Adhearsion scopes via an Adhearsion Plugin.
82
+ # The scope method should have a name and a lambda block that will be executed in the
83
+ # call ExecutionEnvironment context.
84
+ #
85
+ # class AhnPluginDemo < Adhearsion::Plugin
86
+ # dialplan :adh_plugin_demo do
87
+ # speak "hello world"
88
+ # end
89
+ # end
90
+ #
91
+ # You could also defined a dialplan or other scope method as above, but you cannot access
92
+ # the ExecutionEnvironment methods from your specific method due to ruby restrictions
93
+ # when defining methods (the above lambda version should fit any requirement)
94
+ #
95
+ # class AhnPluginDemo < Adhearsion::Plugin
96
+ # dialplan :adh_plugin_demo
97
+ #
98
+ # def self.adh_plugin_demo
99
+ # logger.debug "I can do fun stuff here, but I cannot access methods as speak"
100
+ # logger.debug "I can make an HTTP request"
101
+ # logger.debug "I can log to a specific logging system"
102
+ # logger.debug "I can access database..."
103
+ # logger.debug "but I cannot access call control methods"
104
+ # end
105
+ #
106
+ # end
107
+ #
108
+ define_method name do |method_name, &block|
109
+ case method_name
110
+ when Array
111
+ method_name.each do |method|
112
+ send name, method
113
+ end
114
+ return
115
+ when Hash
116
+ args = method_name
117
+ method_name = method_name[:name]
118
+ end
119
+
120
+ options = args.nil? ? METHODS_OPTIONS : METHODS_OPTIONS.merge(args)
121
+ options[:load] or return
122
+ logger.debug "Adding method #{method_name} to scope #{name}"
123
+ @@methods_container[name].store({:class => self, :method => method_name}, block.nil? ? nil : block)
124
+ end
125
+
126
+ # This method is a helper to retrieve the specific module that holds the user
127
+ # defined scope methods
128
+ define_method "#{name.to_s}_module" do
129
+ Adhearsion::Plugin.methods_scope[name]
130
+ end
131
+
132
+ # Helper to add scope methods to any class/instance
133
+ define_method "add_#{name.to_s}_methods" do |object|
134
+ if object.kind_of?(Module)
135
+ object.send :include, Adhearsion::Plugin.methods_scope[name]
136
+ else
137
+ object.extend Adhearsion::Plugin.methods_scope[name]
138
+ end
139
+ object
140
+ end
141
+ end
142
+
143
+ ##
144
+ # Class method that allows any subclass (any Adhearsion plugin) to register rake tasks.
145
+ #
146
+ # * Example 1:
147
+ #
148
+ # FooBar = Class.new Adhearsion::Plugin do
149
+ # tasks do
150
+ # namespace :foo_bar do
151
+ # desc "Prints the FooBar plugin version"
152
+ # task :version do
153
+ # STDOUT.puts "FooBar plugin v0.1"
154
+ # end
155
+ # end
156
+ # end
157
+ # end
158
+ #
159
+ # * Example 2:
160
+ #
161
+ # FooBar = Class.new Adhearsion::Plugin do
162
+ # tasks do
163
+ # load "tasks/foo_bar.rake"
164
+ # end
165
+ # end
166
+ #
167
+ # = tasks/foo_bar.rake
168
+ #
169
+ # namespace :foo_bar do
170
+ # desc "Prints the FooBar plugin version"
171
+ # task :version do
172
+ # STDOUT.puts "FooBar plugin v0.1"
173
+ # end
174
+ # end
175
+ #
176
+ def tasks
177
+ @@rake_tasks << Proc.new if block_given?
178
+ @@rake_tasks
179
+ end
180
+
181
+ def reset_rake_tasks
182
+ @@rake_tasks = []
183
+ end
184
+
185
+ def load_tasks
186
+ o = Object.new.tap { |o| o.extend Rake::DSL if defined? Rake::DSL }
187
+ tasks.each do |block|
188
+ o.instance_eval &block
189
+ end
190
+ end
191
+
192
+ def methods_scope
193
+ @methods_scope ||= Hash.new { |hash, key| hash[key] = Module.new }
194
+ end
195
+
196
+ # Keep methods to be added
197
+ @@methods_container = Hash.new { |hash, key| hash[key] = MethodsContainer.new }
198
+
199
+ def subclasses
200
+ @subclasses ||= []
201
+ end
202
+
203
+ def inherited(base)
204
+ logger.debug "Detected new plugin: #{base.name}"
205
+ subclasses << base
206
+ end
207
+
208
+ def plugin_name(name = nil)
209
+ if name.nil?
210
+ @plugin_name ||= ActiveSupport::Inflector.underscore(self.name)
211
+ else
212
+ self.plugin_name = name
213
+ end
214
+ end
215
+
216
+ def plugin_name=(name)
217
+ @plugin_name = name
218
+ end
219
+
220
+ def config name = nil
221
+ if block_given?
222
+ if name.nil?
223
+ name = self.plugin_name
224
+ else
225
+ self.plugin_name = name
226
+ end
227
+ ::Loquacious::Configuration.defaults_for name, &Proc.new
228
+ end
229
+
230
+ ::Loquacious.configuration_for plugin_name
231
+ end
232
+
233
+ def show_description
234
+ ::Loquacious::Configuration.help_for plugin_name
235
+ end
236
+
237
+ def load_plugins
238
+ load_methods
239
+ init_plugins
240
+ end
241
+
242
+ # Load plugins scope methods (scope = dialplan, console, etc)
243
+ def load_methods
244
+ unless @@methods_container.empty?
245
+
246
+ @@methods_container.each_pair do |scope, methods|
247
+
248
+ logger.debug "Loading #{methods.length} #{scope} methods"
249
+
250
+ methods.each_pair do |class_method, block|
251
+ klass, method = class_method[:class], class_method[:method]
252
+ if block.nil?
253
+ if klass.respond_to?(method)
254
+ block = klass.method(method).to_proc
255
+ elsif klass.instance_methods.include?(method)
256
+ block = klass.instance_method(method).bind(klass.new)
257
+ else
258
+ logger.warn "Unable to load #{scope} method #{method} from plugin class #{klass}"
259
+ end
260
+ end
261
+
262
+ logger.debug "Defining method #{method}"
263
+ block.nil? and raise NoMethodError.new "Invalid #{scope} method: <#{method}>"
264
+ self.send("#{scope}_module").send(:define_method, method, &block)
265
+ end
266
+ end
267
+
268
+ # We need to extend Console class with the plugin defined methods
269
+ Adhearsion::Console.extend(self.console_module) unless self.console_module.instance_methods.empty?
270
+ end
271
+ end
272
+
273
+ # Recursively initialization of all the loaded plugins
274
+ def init_plugins *args
275
+ initializers.tsort.each do |initializer|
276
+ initializer.run *args
277
+ end
278
+ end
279
+
280
+ def initializers
281
+ @initializers ||= Collection.new
282
+ end
283
+
284
+ # Class method that will be used by subclasses to initialize the plugin
285
+ # @param name Symbol plugin initializer name
286
+ # @param opts Hash
287
+ # * :before specify the plugin to be loaded before another plugin
288
+ # * :after specify the plugin to be loaded after another plugin
289
+ def init(name, opts = {})
290
+ block_given? or raise ArgumentError, "A block must be passed while defining the Plugin initialization process"
291
+ opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
292
+ Adhearsion::Plugin.initializers << Initializer.new(name, nil, opts, &Proc.new)
293
+ end
294
+
295
+ def count
296
+ subclasses.length
297
+ end
298
+
299
+ def add(klass)
300
+ klass.ancestors.include?(self) and subclasses << klass
301
+ end
302
+
303
+ def delete(plugin_name)
304
+ plugin_name.ancestors.include?(self) and plugin_name = plugin_name.plugin_name
305
+ subclasses.delete_if { |plugin| plugin.plugin_name.eql? plugin_name }
306
+ end
307
+
308
+ def delete_all
309
+ @subclasses = nil
310
+ end
311
+ end
312
+
313
+ reset_rake_tasks
314
+
315
+ [:plugin_name, :plugin_name=].each do |method|
316
+ delegate method, :to => "self.class"
317
+ end
318
+ end
319
+ end