adhearsion 2.0.0.beta1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.travis.yml +2 -4
  2. data/CHANGELOG.md +34 -4
  3. data/README.markdown +2 -1
  4. data/Rakefile +22 -1
  5. data/adhearsion.gemspec +1 -0
  6. data/bin/ahn +0 -2
  7. data/features/cli_daemon.feature +2 -0
  8. data/features/cli_restart.feature +19 -0
  9. data/features/cli_start.feature +4 -6
  10. data/features/cli_stop.feature +3 -0
  11. data/features/step_definitions/app_generator_steps.rb +2 -0
  12. data/features/step_definitions/cli_steps.rb +2 -0
  13. data/features/support/aruba_helper.rb +2 -0
  14. data/features/support/env.rb +8 -46
  15. data/features/support/utils.rb +2 -0
  16. data/lib/adhearsion.rb +4 -6
  17. data/lib/adhearsion/call.rb +71 -17
  18. data/lib/adhearsion/call_controller.rb +25 -14
  19. data/lib/adhearsion/call_controller/dial.rb +34 -15
  20. data/lib/adhearsion/call_controller/input.rb +186 -144
  21. data/lib/adhearsion/call_controller/output.rb +10 -6
  22. data/lib/adhearsion/call_controller/record.rb +11 -13
  23. data/lib/adhearsion/call_controller/utility.rb +2 -0
  24. data/lib/adhearsion/calls.rb +4 -2
  25. data/lib/adhearsion/cli.rb +4 -0
  26. data/lib/adhearsion/cli_commands.rb +8 -2
  27. data/lib/adhearsion/configuration.rb +7 -3
  28. data/lib/adhearsion/console.rb +17 -17
  29. data/lib/adhearsion/events.rb +10 -4
  30. data/lib/adhearsion/foundation.rb +9 -0
  31. data/lib/adhearsion/foundation/custom_daemonizer.rb +3 -1
  32. data/lib/adhearsion/foundation/exception_handler.rb +2 -0
  33. data/lib/adhearsion/foundation/libc.rb +2 -0
  34. data/lib/adhearsion/foundation/object.rb +3 -0
  35. data/lib/adhearsion/foundation/thread_safety.rb +5 -11
  36. data/lib/adhearsion/generators.rb +2 -0
  37. data/lib/adhearsion/generators/app/app_generator.rb +2 -0
  38. data/lib/adhearsion/generators/app/templates/README.md +9 -0
  39. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +38 -16
  40. data/lib/adhearsion/generators/app/templates/config/environment.rb +2 -0
  41. data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +5 -3
  42. data/lib/adhearsion/generators/controller/controller_generator.rb +2 -0
  43. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +2 -0
  44. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +2 -0
  45. data/lib/adhearsion/generators/generator.rb +3 -1
  46. data/lib/adhearsion/generators/plugin/plugin_generator.rb +2 -0
  47. data/lib/adhearsion/initializer.rb +31 -17
  48. data/lib/adhearsion/linux_proc_name.rb +2 -0
  49. data/lib/adhearsion/logging.rb +5 -3
  50. data/lib/adhearsion/menu_dsl.rb +2 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match.rb +2 -0
  52. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +2 -0
  53. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +2 -0
  54. data/lib/adhearsion/menu_dsl/match_calculator.rb +2 -0
  55. data/lib/adhearsion/menu_dsl/menu.rb +58 -4
  56. data/lib/adhearsion/menu_dsl/menu_builder.rb +14 -1
  57. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +4 -1
  58. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +2 -0
  59. data/lib/adhearsion/outbound_call.rb +2 -0
  60. data/lib/adhearsion/plugin.rb +9 -7
  61. data/lib/adhearsion/plugin/collection.rb +3 -1
  62. data/lib/adhearsion/plugin/initializer.rb +3 -1
  63. data/lib/adhearsion/process.rb +8 -2
  64. data/lib/adhearsion/punchblock_plugin.rb +3 -1
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +34 -11
  66. data/lib/adhearsion/router.rb +4 -2
  67. data/lib/adhearsion/router/route.rb +2 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -0
  69. data/lib/adhearsion/tasks.rb +2 -0
  70. data/lib/adhearsion/tasks/configuration.rb +2 -0
  71. data/lib/adhearsion/tasks/debugging.rb +8 -0
  72. data/lib/adhearsion/tasks/environment.rb +2 -0
  73. data/lib/adhearsion/tasks/plugins.rb +2 -0
  74. data/lib/adhearsion/tasks/testing.rb +2 -0
  75. data/lib/adhearsion/version.rb +3 -1
  76. data/pre-commit +2 -0
  77. data/spec/adhearsion/call_controller/dial_spec.rb +114 -25
  78. data/spec/adhearsion/call_controller/input_spec.rb +192 -169
  79. data/spec/adhearsion/call_controller/output_spec.rb +26 -12
  80. data/spec/adhearsion/call_controller/record_spec.rb +29 -77
  81. data/spec/adhearsion/call_controller/utility_spec.rb +69 -0
  82. data/spec/adhearsion/call_controller_spec.rb +90 -15
  83. data/spec/adhearsion/call_spec.rb +92 -24
  84. data/spec/adhearsion/calls_spec.rb +9 -7
  85. data/spec/adhearsion/configuration_spec.rb +58 -56
  86. data/spec/adhearsion/console_spec.rb +4 -2
  87. data/spec/adhearsion/events_spec.rb +9 -7
  88. data/spec/adhearsion/generators_spec.rb +3 -1
  89. data/spec/adhearsion/initializer_spec.rb +16 -14
  90. data/spec/adhearsion/logging_spec.rb +11 -9
  91. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +6 -4
  92. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +6 -4
  93. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +3 -1
  94. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +2 -0
  95. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +42 -11
  96. data/spec/adhearsion/menu_dsl/menu_spec.rb +197 -36
  97. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +4 -2
  98. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +5 -3
  99. data/spec/adhearsion/outbound_call_spec.rb +7 -5
  100. data/spec/adhearsion/plugin_spec.rb +19 -15
  101. data/spec/adhearsion/process_spec.rb +12 -7
  102. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +35 -15
  103. data/spec/adhearsion/punchblock_plugin_spec.rb +4 -1
  104. data/spec/adhearsion/router/route_spec.rb +8 -6
  105. data/spec/adhearsion/router_spec.rb +12 -10
  106. data/spec/adhearsion_spec.rb +13 -2
  107. data/spec/capture_warnings.rb +33 -0
  108. data/spec/spec_helper.rb +4 -0
  109. data/spec/support/call_controller_test_helpers.rb +2 -4
  110. data/spec/support/initializer_stubs.rb +8 -5
  111. data/spec/support/logging_helpers.rb +2 -0
  112. data/spec/support/punchblock_mocks.rb +2 -0
  113. metadata +84 -71
  114. data/EVENTS +0 -11
  115. data/lib/adhearsion/call_controller/menu.rb +0 -124
  116. data/lib/adhearsion/foundation/all.rb +0 -8
  117. data/spec/adhearsion/call_controller/menu_spec.rb +0 -120
  118. data/spec/adhearsion/menu_dsl_spec.rb +0 -12
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  module MenuDSL
3
5
 
@@ -8,11 +10,12 @@ module Adhearsion
8
10
  def initialize
9
11
  @patterns = []
10
12
  @menu_callbacks = {}
13
+ @context = nil
11
14
  end
12
15
 
13
16
  def build(&block)
14
17
  @context = eval "self", block.binding
15
- instance_eval &block
18
+ instance_eval(&block)
16
19
  end
17
20
 
18
21
  def match(*args, &block)
@@ -35,8 +38,13 @@ module Adhearsion
35
38
  @patterns
36
39
  end
37
40
 
41
+ def has_matchers?
42
+ @patterns.size > 0
43
+ end
44
+
38
45
  def execute_hook_for(symbol, input)
39
46
  callback = @menu_callbacks[symbol]
47
+ return unless callback
40
48
  @context.instance_exec input, &callback
41
49
  end
42
50
 
@@ -55,6 +63,11 @@ module Adhearsion
55
63
  @menu_callbacks[:failure] = block
56
64
  end
57
65
 
66
+ def validator(&block)
67
+ raise LocalJumpError, "Must supply a block!" unless block_given?
68
+ @menu_callbacks[:validator] = block
69
+ end
70
+
58
71
  def calculate_matches_for(result)
59
72
  CalculatedMatchCollection.new.tap do |collection|
60
73
  weighted_match_calculators.each do |pattern|
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  module MenuDSL
3
5
 
@@ -42,7 +44,8 @@ module Adhearsion
42
44
  power = 0
43
45
  while num < last
44
46
  ones_count = 10**power - 1
45
- matches.concat ([num, first].max..[num + ones_count, last].min).to_a
47
+ range = ([num, first].max..[num + ones_count, last].min).to_a
48
+ matches.concat range
46
49
  num *= 10
47
50
  power += 1
48
51
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  module MenuDSL
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class OutboundCall < Call
3
5
  attr_reader :dial_command
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
 
3
5
  # Plugin is the core of extension of Adhearsion framework and provides the easiest
@@ -99,9 +101,9 @@ module Adhearsion
99
101
  end
100
102
 
101
103
  def load_tasks
102
- o = Object.new.tap { |o| o.extend Rake::DSL if defined? Rake::DSL }
104
+ container = Object.new.tap { |o| o.extend Rake::DSL if defined? Rake::DSL }
103
105
  tasks.each do |block|
104
- o.instance_eval &block
106
+ container.instance_eval(&block)
105
107
  end
106
108
  end
107
109
 
@@ -124,7 +126,7 @@ module Adhearsion
124
126
  end
125
127
 
126
128
  def inherited(base)
127
- logger.debug "Detected new plugin: #{base.name}"
129
+ logger.info "Detected new plugin: #{base.name}"
128
130
  subclasses << base
129
131
  end
130
132
 
@@ -159,15 +161,15 @@ module Adhearsion
159
161
  end
160
162
 
161
163
  # Recursively initialization of all the loaded plugins
162
- def init_plugins *args
164
+ def init_plugins(*args)
163
165
  initializers.tsort.each do |initializer|
164
- initializer.run *args
166
+ initializer.run(*args)
165
167
  end
166
168
  end
167
169
 
168
- def run_plugins *args
170
+ def run_plugins(*args)
169
171
  runners.tsort.each do |runner|
170
- runner.run *args
172
+ runner.run(*args)
171
173
  end
172
174
  end
173
175
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'tsort'
2
4
 
3
5
  module Adhearsion
@@ -8,7 +10,7 @@ module Adhearsion
8
10
  alias :tsort_each_node :each
9
11
 
10
12
  def tsort_each_child(child, &block)
11
- select { |i| i.before == child.name || i.name == child.after }.each &block
13
+ select { |i| i.before == child.name || i.name == child.after }.each(&block)
12
14
  end
13
15
 
14
16
  def +(other)
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class Plugin
3
5
  class Initializer
@@ -21,7 +23,7 @@ module Adhearsion
21
23
  end
22
24
 
23
25
  def run(*args)
24
- @context.instance_exec *args, &block
26
+ @context.instance_exec(*args, &block)
25
27
  end
26
28
 
27
29
  def bind(context)
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'state_machine'
2
4
  require 'singleton'
3
5
  require 'socket'
@@ -41,7 +43,7 @@ module Adhearsion
41
43
  end
42
44
 
43
45
  event :stop do
44
- transition :rejecting => :stopped
46
+ transition all => :stopped
45
47
  end
46
48
 
47
49
  event :force_stop do
@@ -81,12 +83,16 @@ module Adhearsion
81
83
  Events.trigger_immediately :shutdown
82
84
 
83
85
  Console.stop
86
+
87
+ logger.info "Adhearsion shut down"
84
88
  end
85
89
 
86
90
  def stop_when_zero_calls
91
+ i = 0
87
92
  until Adhearsion.active_calls.count == 0
88
- logger.trace "Stop requested but we still have #{Adhearsion.active_calls.count} active calls."
93
+ logger.info "Stop requested but we still have #{Adhearsion.active_calls.count} active calls." if (i % 50) == 0
89
94
  sleep 0.2
95
+ i += 1
90
96
  end
91
97
  final_shutdown
92
98
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class PunchblockPlugin < Plugin
3
5
  extend ActiveSupport::Autoload
@@ -41,7 +43,7 @@ module Adhearsion
41
43
  end
42
44
 
43
45
  def execute_component(command, timeout = 60)
44
- client.execute_command command
46
+ client.execute_command command, :async => true
45
47
  response = command.response timeout
46
48
  raise response if response.is_a? Exception
47
49
  command
@@ -1,3 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ require 'blather'
4
+
1
5
  module Adhearsion
2
6
  class PunchblockPlugin
3
7
  class Initializer
@@ -8,12 +12,15 @@ module Adhearsion
8
12
  class << self
9
13
  def init
10
14
  self.config = Adhearsion.config[:punchblock]
15
+
16
+ username = self.config.username
11
17
  connection_class = case (self.config.platform || :xmpp)
12
18
  when :xmpp
13
- username = [self.config.username, resource].join('/')
19
+ username = Blather::JID.new username
20
+ username = Blather::JID.new username.node, username.domain, resource unless username.resource
21
+ username = username.to_s
14
22
  ::Punchblock::Connection::XMPP
15
23
  when :asterisk
16
- username = self.config.username
17
24
  ::Punchblock::Connection::Asterisk
18
25
  end
19
26
 
@@ -50,7 +57,7 @@ module Adhearsion
50
57
 
51
58
  # Handle events from Punchblock via events system
52
59
  self.client.register_event_handler do |event|
53
- Events.trigger :punchblock, event
60
+ handle_event event
54
61
  end
55
62
 
56
63
  Events.punchblock ::Punchblock::Connection::Connected do |event|
@@ -81,6 +88,7 @@ module Adhearsion
81
88
  blocker = ConditionVariable.new
82
89
 
83
90
  Events.punchblock ::Punchblock::Connection::Connected do
91
+ Adhearsion::Process.booted
84
92
  m.synchronize { blocker.broadcast }
85
93
  end
86
94
 
@@ -103,15 +111,21 @@ module Adhearsion
103
111
  client.run
104
112
  rescue ::Punchblock::DisconnectedError => e
105
113
  # We only care about disconnects if the process is up or booting
106
- if [:booting, :running].include? Adhearsion::Process.state_name
107
- self.attempts += 1
108
- Adhearsion::Process.reset unless Adhearsion::Process.state_name == :booting
109
- logger.error "Connection lost. Attempting reconnect #{self.attempts} of #{self.config.reconnect_attempts}"
110
- sleep self.config.reconnect_timer
111
- retry unless self.attempts >= self.config.reconnect_attempts
112
- logger.fatal "Connection retry attempts exceeded"
113
- raise e
114
+ return unless [:booting, :running].include? Adhearsion::Process.state_name
115
+
116
+ Adhearsion::Process.reset unless Adhearsion::Process.state_name == :booting
117
+
118
+ self.attempts += 1
119
+
120
+ if self.attempts >= self.config.reconnect_attempts
121
+ logger.fatal "Connection lost. Connection retry attempts exceeded."
122
+ Adhearsion::Process.stop!
123
+ return
114
124
  end
125
+
126
+ logger.error "Connection lost. Attempting reconnect #{self.attempts} of #{self.config.reconnect_attempts}"
127
+ sleep self.config.reconnect_timer
128
+ retry
115
129
  rescue ::Punchblock::ProtocolError => e
116
130
  logger.fatal "The connection failed due to a protocol error: #{e.name}."
117
131
  raise e
@@ -123,6 +137,7 @@ module Adhearsion
123
137
  call = Adhearsion.active_calls.from_offer offer
124
138
  case Adhearsion::Process.state_name
125
139
  when :booting, :rejecting
140
+ logger.info "Declining call because the process is not yet running."
126
141
  call.reject :decline
127
142
  when :running
128
143
  call.accept
@@ -142,6 +157,14 @@ module Adhearsion
142
157
  end
143
158
  end
144
159
 
160
+ def handle_event(event)
161
+ Events.trigger :punchblock, event
162
+ case event
163
+ when Punchblock::Event::Asterisk::AMI::Event
164
+ Events.trigger :ami, event
165
+ end
166
+ end
167
+
145
168
  def resource
146
169
  [Adhearsion::Process.fqdn, ::Process.pid].join '-'
147
170
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class Router
3
5
  extend ActiveSupport::Autoload
@@ -8,7 +10,7 @@ module Adhearsion
8
10
 
9
11
  def initialize(&block)
10
12
  @routes = []
11
- instance_exec &block
13
+ instance_exec(&block)
12
14
  end
13
15
 
14
16
  def route(*args, &block)
@@ -23,7 +25,7 @@ module Adhearsion
23
25
 
24
26
  def handle(call)
25
27
  return unless route = match(call)
26
- logger.debug "Call #{call.id} passing through router matched route #{route}"
28
+ logger.info "Call #{call.id} selected route \"#{route.name}\" (#{route.target})"
27
29
  route.dispatcher
28
30
  end
29
31
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class Router
3
5
  class Route
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'pathname'
2
4
  require 'rbconfig'
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'adhearsion'
2
4
 
3
5
  Dir[File.join(File.dirname(__FILE__), "tasks/*.rb")].each do |file|
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  namespace :config do
2
4
  desc "Show configuration values; accepts a parameter: [nil|platform|<plugin-name>|all]"
3
5
  task :show, [:name] => [:environment] do |t, args|
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ task :debugging => :environment do
4
+ initializer = Adhearsion::Initializer.new
5
+ puts
6
+ puts "Some info about your application environment:"
7
+ puts initializer.debugging_items.join("\n\n")
8
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  task :environment do
2
4
  require 'adhearsion/punchblock_plugin'
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  desc "List the configured plugins"
2
4
  task :plugins => :environment do |t,args|
3
5
  if Adhearsion::Plugin.subclasses.length > 0
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  begin
2
4
  require 'rspec/core/rake_task'
3
5
  rescue LoadError
@@ -1,5 +1,7 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion #:nodoc:
2
- VERSION = '2.0.0.beta1'
4
+ VERSION = '2.0.0.rc1'
3
5
 
4
6
  class PkgVersion
5
7
  include Comparable
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ rake encodeify
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Adhearsion
@@ -13,11 +15,14 @@ module Adhearsion
13
15
  let(:second_other_call_id) { new_uuid }
14
16
  let(:second_other_mock_call) { flexmock OutboundCall.new, :id => second_other_call_id }
15
17
 
16
- let(:mock_end) { flexmock Punchblock::Event::End.new, :reason => :hangup }
17
18
  let(:mock_answered) { Punchblock::Event::Answered.new }
18
19
 
19
20
  let(:latch) { CountDownLatch.new 1 }
20
21
 
22
+ def mock_end(reason = :hangup)
23
+ flexmock Punchblock::Event::End.new, :reason => reason
24
+ end
25
+
21
26
  describe "#dial" do
22
27
  before do
23
28
  other_mock_call
@@ -48,14 +53,16 @@ module Adhearsion
48
53
 
49
54
  describe "without a block" do
50
55
  before do
51
- flexmock(other_mock_call).should_receive(:dial).once
56
+ flexmock(other_mock_call).should_receive(:dial).once.with(to, options)
52
57
  flexmock(other_mock_call).should_receive(:hangup).once
53
58
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
54
59
  end
55
60
 
61
+ let(:options) { { :foo => :bar } }
62
+
56
63
  def dial_in_thread
57
64
  Thread.new do
58
- status = subject.dial to
65
+ status = subject.dial to, options
59
66
  latch.countdown!
60
67
  status
61
68
  end
@@ -113,13 +120,29 @@ module Adhearsion
113
120
 
114
121
  sleep 0.5
115
122
 
116
- other_mock_call << mock_end
123
+ other_mock_call << mock_end(:reject)
117
124
 
118
125
  latch.wait(2).should be_true
119
126
 
120
127
  t.join
121
128
  status = t.value
122
- status.result.should == :no_answer
129
+ status.result.should be == :no_answer
130
+ end
131
+ end
132
+
133
+ context "when the call ends with an error" do
134
+ it "has an overall dial status of :error" do
135
+ t = dial_in_thread
136
+
137
+ sleep 0.5
138
+
139
+ other_mock_call << mock_end(:error)
140
+
141
+ latch.wait(2).should be_true
142
+
143
+ t.join
144
+ status = t.value
145
+ status.result.should be == :error
123
146
  end
124
147
  end
125
148
 
@@ -138,28 +161,32 @@ module Adhearsion
138
161
 
139
162
  t.join
140
163
  status = t.value
141
- status.result.should == :answer
164
+ status.result.should be == :answer
142
165
  end
143
166
  end
144
167
  end
145
168
 
146
169
  describe "with multiple third parties specified" do
170
+ let(:options) { {} }
171
+ let(:other_options) { options }
172
+ let(:second_other_options) { options }
173
+
147
174
  before do
148
175
  second_other_mock_call
149
176
 
150
177
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call, second_other_mock_call
151
178
 
152
- flexmock(other_mock_call).should_receive(:dial).once
179
+ flexmock(other_mock_call).should_receive(:dial).once.with(to, other_options)
153
180
  flexmock(other_mock_call).should_receive(:hangup).once
154
181
 
155
- flexmock(second_other_mock_call).should_receive(:dial).once
182
+ flexmock(second_other_mock_call).should_receive(:dial).once.with(second_to, second_other_options)
156
183
  flexmock(second_other_mock_call).should_receive(:join).never
157
184
  flexmock(second_other_mock_call).should_receive(:hangup).once
158
185
  end
159
186
 
160
187
  def dial_in_thread
161
188
  Thread.new do
162
- status = subject.dial [to, second_to]
189
+ status = subject.dial [to, second_to], options
163
190
  latch.countdown!
164
191
  status
165
192
  end
@@ -213,24 +240,94 @@ module Adhearsion
213
240
  status.calls.each { |c| c.should be_a OutboundCall }
214
241
  end
215
242
 
243
+ describe "with options overrides" do
244
+ let(:options) do
245
+ {
246
+ :from => 'foo',
247
+ :timeout => 3000,
248
+ :headers => {
249
+ :x_foo => 'bar'
250
+ }
251
+ }
252
+ end
253
+
254
+ let(:dial_other_options) do
255
+ {
256
+ :foo => 'bar',
257
+ :headers => {
258
+ :x_foo => 'buzz'
259
+ }
260
+ }
261
+ end
262
+
263
+ let(:other_options) do
264
+ {
265
+ :from => 'foo',
266
+ :timeout => 3000,
267
+ :foo => 'bar',
268
+ :headers => {
269
+ :x_foo => 'buzz'
270
+ }
271
+
272
+ }
273
+ end
274
+
275
+ let(:dial_second_other_options) do
276
+ {
277
+ :timeout => 5000,
278
+ :headers => {
279
+ :x_bar => 'barbuzz'
280
+ }
281
+ }
282
+ end
283
+
284
+ let(:second_other_options) do
285
+ {
286
+ :from => 'foo',
287
+ :timeout => 5000,
288
+ :headers => {
289
+ :x_foo => 'bar',
290
+ :x_bar => 'barbuzz'
291
+ }
292
+ }
293
+ end
294
+
295
+ it "with multiple destinations as an hash, with overrides for each, and an options hash, it dials each call with specified options" do
296
+ t = Thread.new do
297
+ subject.dial({
298
+ to => dial_other_options,
299
+ second_to => dial_second_other_options
300
+ }, options)
301
+ latch.countdown!
302
+ end
303
+
304
+ latch.wait(1).should be_false
305
+ other_mock_call << mock_end
306
+ latch.wait(1).should be_false
307
+ second_other_mock_call << mock_end
308
+ latch.wait(2).should be_true
309
+ t.join
310
+ end
311
+ end
312
+
216
313
  context "when all calls are rejected" do
217
314
  it "has an overall dial status of :no_answer" do
218
315
  t = dial_in_thread
219
316
 
220
317
  sleep 0.5
221
318
 
222
- other_mock_call << mock_end
223
- second_other_mock_call << mock_end
319
+ other_mock_call << mock_end(:reject)
320
+ second_other_mock_call << mock_end(:reject)
224
321
 
225
322
  latch.wait(2).should be_true
226
323
 
227
324
  t.join
228
325
  status = t.value
229
- status.result.should == :no_answer
326
+ status.result.should be == :no_answer
230
327
  end
231
328
  end
232
329
 
233
- context "when a call is answered and joined" do
330
+ context "when a call is answered and joined, and the other ends with an error" do
234
331
  it "has an overall dial status of :answer" do
235
332
  flexmock(other_mock_call).should_receive(:join).once.with(call)
236
333
  flexmock(second_other_mock_call).should_receive(:hangup).once
@@ -242,13 +339,13 @@ module Adhearsion
242
339
  other_mock_call << mock_answered
243
340
  other_mock_call << mock_end
244
341
 
245
- second_other_mock_call << mock_end
342
+ second_other_mock_call << mock_end(:error)
246
343
 
247
344
  latch.wait(1).should be_true
248
345
 
249
346
  t.join
250
347
  status = t.value
251
- status.result.should == :answer
348
+ status.result.should be == :answer
252
349
  end
253
350
  end
254
351
  end
@@ -271,20 +368,12 @@ module Adhearsion
271
368
 
272
369
  latch.wait
273
370
  time = Time.now - time
274
- time.to_i.should == timeout
371
+ time.to_i.should be == timeout
275
372
  t.join
276
373
  status = t.value
277
- status.result.should == :timeout
374
+ status.result.should be == :timeout
278
375
  end
279
376
  end
280
-
281
- describe "with a block" do
282
- it "uses the block as the controller for the new call"
283
-
284
- it "joins the new call to the existing call once the block returns"
285
-
286
- it "does not try to join the calls if the new call is hungup when the block returns"
287
- end
288
377
  end#describe #dial
289
378
  end
290
379
  end