adhearsion 2.0.0.beta1 → 2.0.0.rc1

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 (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