adhearsion 1.2.6 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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