adhearsion 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +29 -0
  4. data/Gemfile +0 -2
  5. data/Guardfile +2 -2
  6. data/README.markdown +3 -6
  7. data/Rakefile +1 -1
  8. data/adhearsion.gemspec +7 -2
  9. data/features/cli_create.feature +85 -7
  10. data/features/plugin_generator.feature +4 -0
  11. data/features/step_definitions/app_generator_steps.rb +8 -1
  12. data/lib/adhearsion.rb +6 -3
  13. data/lib/adhearsion/call.rb +101 -30
  14. data/lib/adhearsion/call_controller.rb +40 -12
  15. data/lib/adhearsion/call_controller/dial.rb +119 -36
  16. data/lib/adhearsion/call_controller/input.rb +11 -5
  17. data/lib/adhearsion/call_controller/output.rb +47 -33
  18. data/lib/adhearsion/call_controller/output/async_player.rb +3 -2
  19. data/lib/adhearsion/call_controller/output/formatter.rb +7 -2
  20. data/lib/adhearsion/call_controller/output/player.rb +2 -2
  21. data/lib/adhearsion/call_controller/record.rb +16 -13
  22. data/lib/adhearsion/call_controller/utility.rb +3 -3
  23. data/lib/adhearsion/calls.rb +21 -8
  24. data/lib/adhearsion/cli_commands/ahn_command.rb +1 -0
  25. data/lib/adhearsion/configuration.rb +2 -2
  26. data/lib/adhearsion/console.rb +3 -2
  27. data/lib/adhearsion/generators.rb +7 -9
  28. data/lib/adhearsion/generators/app/app_generator.rb +12 -2
  29. data/lib/adhearsion/generators/app/templates/Gemfile.erb +7 -9
  30. data/lib/adhearsion/generators/app/templates/README.md +0 -19
  31. data/lib/adhearsion/generators/app/templates/adhearsion.erb +37 -0
  32. data/lib/adhearsion/generators/app/templates/config/environment.rb +6 -1
  33. data/lib/adhearsion/generators/app/templates/events.erb +18 -0
  34. data/lib/adhearsion/generators/app/templates/routes.erb +19 -0
  35. data/lib/adhearsion/generators/app/templates/{lib/simon_game.rb → simon_game.rb} +0 -0
  36. data/lib/adhearsion/generators/app/templates/{spec/call_controllers/simon_game_spec.rb → simon_game_spec.rb} +0 -0
  37. data/lib/adhearsion/generators/controller/controller_generator.rb +2 -2
  38. data/lib/adhearsion/generators/controller/templates/lib/{controller.rb → controller.rb.erb} +0 -0
  39. data/lib/adhearsion/generators/controller/templates/spec/{controller_spec.rb → controller_spec.rb.erb} +0 -0
  40. data/lib/adhearsion/generators/plugin/plugin_generator.rb +16 -15
  41. data/lib/adhearsion/generators/plugin/templates/gitignore +17 -0
  42. data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +1 -1
  43. data/lib/adhearsion/initializer.rb +14 -2
  44. data/lib/adhearsion/logging.rb +1 -0
  45. data/lib/adhearsion/outbound_call.rb +3 -7
  46. data/lib/adhearsion/punchblock_plugin/initializer.rb +3 -2
  47. data/lib/adhearsion/router/openended_route.rb +1 -1
  48. data/lib/adhearsion/router/route.rb +4 -3
  49. data/lib/adhearsion/version.rb +1 -1
  50. data/spec/adhearsion/call_controller/dial_spec.rb +811 -79
  51. data/spec/adhearsion/call_controller/output/formatter_spec.rb +13 -1
  52. data/spec/adhearsion/call_controller/output_spec.rb +35 -1
  53. data/spec/adhearsion/call_controller_spec.rb +174 -18
  54. data/spec/adhearsion/call_spec.rb +423 -39
  55. data/spec/adhearsion/calls_spec.rb +19 -3
  56. data/spec/adhearsion/outbound_call_spec.rb +88 -45
  57. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +3 -3
  58. data/spec/adhearsion/router/route_spec.rb +2 -2
  59. data/spec/spec_helper.rb +2 -0
  60. metadata +92 -77
  61. data/features/app_generator.feature +0 -49
  62. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +0 -71
  63. data/lib/adhearsion/generators/plugin/templates/.gitignore +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61315b79e7d1320dea8413e2b5c26f9b1c953551
4
- data.tar.gz: 2935a1722a541274a596fb2a35d5e3e5b7d05a23
3
+ metadata.gz: 9155fcf025c8ed90e014e5262b70387e46e31937
4
+ data.tar.gz: dcb5d1f0104f854e3b487f34cf199e3134d5d38d
5
5
  SHA512:
6
- metadata.gz: d4210b4cae9891cc1f5f2e6ecca89e7e4b0f371287d0726f3a876b99910e35b887758d1f5ab6e59a2afca1c58c9e270cb4377c531c55d4d79e1c9454773319af
7
- data.tar.gz: 8302e6d6500220edd4892b557f79b1c5fcdcaa1d68771a3bb6db2f7094f36912c349c9f297adaeef8b4ba572aa0dadddf2fc2121c09e1dd592d20bff843d625d
6
+ metadata.gz: 00076ee1fdd8a0d208dc1895dbb74f33f5c0adc23a2348248c0c8938378715d2bd40b7f8bfe64e42b05d2cd3e9594a0159d00af5b64f8e3285e37d2336249011
7
+ data.tar.gz: 7b1e9f478d6860f662e485a1c5add9e69ed0d1c2cf0f1d038ed661c47e2fd791224855bd452d89c79c09b18ebec16988df30af6650fb03870b77cde1f190ab14
@@ -1,14 +1,14 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.2
4
3
  - 1.9.3
5
4
  - 2.0.0
5
+ - 2.1.0
6
6
  - jruby-19mode
7
- - rbx-19mode
7
+ - rbx-2.1.1
8
8
  - ruby-head
9
9
  matrix:
10
10
  allow_failures:
11
- - rvm: rbx-19mode
11
+ - rvm: rbx-2.1.1
12
12
  - rvm: ruby-head
13
13
  env: ARUBA_TIMEOUT=120 RAILS_ENV=development AHN_ENV=development
14
14
  notifications:
@@ -1,5 +1,34 @@
1
1
  # [develop](https://github.com/adhearsion/adhearsion)
2
2
 
3
+ # [2.5.0](https://github.com/adhearsion/adhearsion/compare/v2.4.0...v2.5.0) - [2014-02-03](https://rubygems.org/gems/adhearsion/versions/2.5.0)
4
+ * **Change: Ruby 1.9.2 is no longer supported**
5
+ * Change: Set default call post-hangup lifetime to one second for better out of the box performance.
6
+ * Feature: Allow stopping all components executed by a controller when passing from it (`#hard_pass`) or at will (`#stop_all_components`)
7
+ * Feature: Generated plugins include a .gitignore file
8
+ * Feature: Detect something like "1234#" as characters instead of text
9
+ * Feature: Add `--empty` switch to app generator for power users. Generates an app with less fluff.
10
+ * Feature: Default generated applications with config appropriate for [Telephony Dev Box](http://github.com/mojolingo/Telephony-Dev-Box)
11
+ * Feature: Allow preventing automatic call hangup after controller completion. Set `Call#auto_hangup = false` prior to controller termination.
12
+ * Feature: Allow specifying a pre-join callback to #dial
13
+ * Feature: Allow specifying ringback to `#dial` as either a list or a proc
14
+ * Feature: Allow passing `:join_options` parameter to `#dial` to specify the kind of join to perform.
15
+ * Feature: Allow passing `:join_target` parameter to `#dial` to specify who to join to (eg a mixer).
16
+ * Feature: Generated apps now encourage storing most app code in `app/`, which is in the load path. Nothing in this directory is auto-loaded, but can be easily required without messy relative paths.
17
+ * Feature: Routes and event handlers are now split out of main config file for readability. They may be retained in the main config file for existing apps.
18
+ * Feature: Add `Call#end_code`, which specifies the platform code for the call end event.
19
+ * Feature: Allow XMPP messages to be sent via Punchblock
20
+ * Bugfix: Don't block shutdown waiting for the console to terminate
21
+ * Bugfix: Ensure that splitting a dial rejoined to an alternative target (eg a mixer) or merged with another dial can still be split properly.
22
+ * Bugfix: Ensure that hungup calls don't prevent dial splits/merges.
23
+ * Bugfix: Ensure that merged dials which are split and rejoined all get rejoined to the mixer.
24
+ * Bugfix: Ensure that `Call#peers` are correctly maintained - this was broken by Rayo moving to full call URIs in joined/unjoined events
25
+ * Bugfix: Ensure that failure to dial a call kills the actor
26
+ * Bugfix: Adhearsion call actors should do recursion detection on #inspect
27
+ * Bugfix: Fix controller block context on JRuby
28
+ * Bugfix: Require only Bundler groups appropriate to application environment
29
+ * Bugfix: Call event handlers now cannot crash the call, and no longer stop on return value
30
+ * Bugfix: Call controllers are gracefully terminated when a command is attempted against a dead call
31
+
3
32
  # [2.4.0](https://github.com/adhearsion/adhearsion/compare/v2.3.5...v2.4.0) - [2013-08-29](https://rubygems.org/gems/adhearsion/versions/2.4.0)
4
33
  * Deprecation: Ruby 1.9.2 support is deprecated and will be dropped in a future version of Adhearsion
5
34
  * Deprecation: Some media options from Punchblock config are now overriden by other core config, and will eventually be removed.
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- gem 'activesupport', '~> 3.0' if RUBY_VERSION == "1.9.2"
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  ENV['SKIP_RCOV'] = 'true'
2
2
 
3
3
  group 'rspec' do
4
- guard 'rspec', :cli => '--format documentation' do
4
+ guard 'rspec', cmd: 'bundle exec rspec' do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
7
  watch('spec/spec_helper.rb') { "spec/" }
@@ -9,7 +9,7 @@ group 'rspec' do
9
9
  end
10
10
 
11
11
  group 'cucumber' do
12
- guard 'cucumber', :cli => '--profile guard' do
12
+ guard 'cucumber', :cli => '--profile guard', all_on_start: false do
13
13
  watch(%r{^features/.+\.feature$})
14
14
  watch(%r{^features/support/.+$}) { 'features' }
15
15
  watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
@@ -23,18 +23,15 @@ Adhearsion rests above a lower-level telephony platform, for example [Asterisk](
23
23
 
24
24
  ## Requirements
25
25
 
26
- * Ruby 1.9.2+ or JRuby 1.7.0+
26
+ * Ruby 1.9.3+ or JRuby 1.7.0+
27
+ * [ruby_speech dependencies](https://github.com/benlangfeld/ruby_speech#dependencies)
27
28
  * A VoIP platform:
28
29
  * Asterisk 1.8+
29
30
  * FreeSWITCH
30
31
  * A Rayo server (Prism 11+ with rayo-server, or FreeSWITCH with mod_rayo)
31
32
  * An interest in building cool new things
32
33
 
33
- \* Support for Ruby 1.9.2 is deprecated, and requires locking your application to ActiveSupport 3.x as follows:
34
-
35
- ```ruby
36
- gem 'active_support', '~> 3.0'
37
- ```
34
+ **Ruby 1.9.2 is no longer supported by Adhearsion or the Ruby core team. You should upgrade to Ruby 1.9.3 or Ruby 2 as a matter of urgency in order to continue receiving security fixes.**
38
35
 
39
36
  ## Install
40
37
 
data/Rakefile CHANGED
@@ -29,7 +29,7 @@ end
29
29
  begin
30
30
  require 'yard'
31
31
  YARD::Rake::YardocTask.new do |t|
32
- t.files = ['lib/**/*.rb'] + %w[README.markdown TODO.markdown LICENSE]
32
+ t.files = ['lib/**/*.rb']
33
33
  end
34
34
  rescue LoadError
35
35
  STDERR.puts "\nCould not require() YARD! Install with 'gem install yard' to get the 'yardoc' task\n\n"
@@ -12,6 +12,10 @@ Gem::Specification.new do |s|
12
12
  s.summary = "Adhearsion, open-source telephony development framework"
13
13
  s.description = "Adhearsion is an open-source telephony development framework"
14
14
 
15
+ s.license = 'MIT'
16
+
17
+ s.required_ruby_version = '>= 1.9.3'
18
+
15
19
  s.files = `git ls-files`.split("\n")
16
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -25,11 +29,11 @@ Gem::Specification.new do |s|
25
29
  s.add_runtime_dependency 'deep_merge'
26
30
  s.add_runtime_dependency 'ffi', ["~> 1.0"]
27
31
  s.add_runtime_dependency 'girl_friday'
28
- s.add_runtime_dependency 'has-guarded-handlers', ["~> 1.5"]
32
+ s.add_runtime_dependency 'has-guarded-handlers', ["~> 1.6"]
29
33
  s.add_runtime_dependency 'jruby-openssl' if RUBY_PLATFORM == 'java'
30
34
  s.add_runtime_dependency 'logging', ["~> 1.8"]
31
35
  s.add_runtime_dependency 'pry'
32
- s.add_runtime_dependency 'punchblock', ["~> 2.0"]
36
+ s.add_runtime_dependency 'punchblock', ["~> 2.3"]
33
37
  s.add_runtime_dependency 'rake'
34
38
  s.add_runtime_dependency 'ruby_speech', ["~> 2.0"]
35
39
  s.add_runtime_dependency 'thor', "~> 0.18.0"
@@ -43,6 +47,7 @@ Gem::Specification.new do |s|
43
47
  s.add_development_dependency 'simplecov'
44
48
  s.add_development_dependency 'simplecov-rcov'
45
49
  s.add_development_dependency 'yard'
50
+ s.add_development_dependency 'guard-yard'
46
51
  s.add_development_dependency 'coveralls'
47
52
  s.add_development_dependency 'timecop'
48
53
  end
@@ -3,18 +3,96 @@ Feature: Adhearsion Ahn CLI (Create)
3
3
  I want the ahn command to allow creating an app
4
4
  So that I can write an Adhearsion app
5
5
 
6
- Scenario: Command create with correct arguments
6
+ Scenario: Generate application with valid layout
7
7
  When I run `ahn create path/somewhere`
8
8
  And I cd to "path/somewhere"
9
- Then the following files should exist:
10
- | Gemfile |
11
- | README.md |
12
- | Rakefile |
9
+ Then the following directories should exist:
10
+ | app/call_controllers |
11
+ | lib |
12
+ | config |
13
+ | script |
14
+ | spec |
15
+ | spec/call_controllers |
16
+ | spec/support |
17
+
18
+ And the following files should exist:
19
+ | .gitignore |
20
+ | .rspec |
21
+ | app/call_controllers/simon_game.rb |
22
+ | config/adhearsion.rb |
23
+ | config/environment.rb |
24
+ | config/events.rb |
25
+ | config/routes.rb |
26
+ | Gemfile |
27
+ | script/ahn |
28
+ | spec/spec_helper.rb |
29
+ | spec/call_controllers/simon_game_spec.rb |
30
+ | README.md |
31
+ | Rakefile |
32
+ | Procfile |
33
+
34
+ And the file "config/adhearsion.rb" should contain each of these content parts:
35
+ """
36
+ Adhearsion.config
37
+ logging.level
38
+ config.punchblock
39
+ """
40
+ And the file "config/events.rb" should contain each of these content parts:
41
+ """
42
+ Adhearsion::Events.draw do
43
+ """
44
+ And the file "config/routes.rb" should contain each of these content parts:
45
+ """
46
+ Adhearsion.router
47
+ """
48
+ And the file "README.md" should contain each of these content parts:
49
+ """
50
+ Start your new app with
51
+ AGI(agi
52
+ """
53
+ And the file "Rakefile" should contain "adhearsion/tasks"
54
+ And the file "Gemfile" should contain each of these content parts:
55
+ """
56
+ source 'https://rubygems.org
57
+ gem 'adhearsion-asr'
58
+ """
59
+ And the file "app/call_controllers/simon_game.rb" should contain "class SimonGame"
60
+ And the file "script/ahn" should contain "require 'adhearsion'"
61
+
62
+ Scenario: Generate application --empty
63
+ When I run `ahn create path/somewhere --empty`
64
+ And I cd to "path/somewhere"
65
+ Then the following directories should exist:
66
+ | app/call_controllers |
67
+ | lib |
68
+ | config |
69
+ | script |
70
+ | spec |
71
+ | spec/call_controllers |
72
+ | spec/support |
73
+
74
+ And the following files should exist:
75
+ | .gitignore |
76
+ | .rspec |
13
77
  | config/adhearsion.rb |
14
78
  | config/environment.rb |
79
+ | config/events.rb |
80
+ | config/routes.rb |
81
+ | Gemfile |
82
+ | script/ahn |
15
83
  | spec/spec_helper.rb |
16
- And the file "config/adhearsion.rb" should contain "Adhearsion.router"
17
- Then the exit status should be 0
84
+ | README.md |
85
+ | Rakefile |
86
+ | Procfile |
87
+
88
+ And the following files should not exist:
89
+ | app/call_controllers/simon_game.rb |
90
+ | spec/call_controllers/simon_game_spec.rb |
91
+
92
+ And the file "config/events.rb" should not contain each of these content parts:
93
+ """
94
+ # Register global handlers for events
95
+ """
18
96
 
19
97
  Scenario: Running create with no arguments
20
98
  When I run `ahn create`
@@ -11,6 +11,7 @@ Feature: Adhearsion plugin generator
11
11
  | test_plugin/lib/test_plugin |
12
12
  | test_plugin/spec |
13
13
  And the following files should exist:
14
+ | test_plugin/.gitignore |
14
15
  | test_plugin/test_plugin.gemspec |
15
16
  | test_plugin/Rakefile |
16
17
  | test_plugin/README.md |
@@ -21,6 +22,7 @@ Feature: Adhearsion plugin generator
21
22
  | test_plugin/lib/test_plugin/controller_methods.rb |
22
23
  | test_plugin/spec/spec_helper.rb |
23
24
  | test_plugin/spec/test_plugin/controller_methods_spec.rb |
25
+ And the file "test_plugin/.gitignore" should contain "Gemfile.lock"
24
26
  And the file "test_plugin/test_plugin.gemspec" should contain "test_plugin/version"
25
27
  And the file "test_plugin/README.md" should contain "TestPlugin"
26
28
  And the file "test_plugin/lib/test_plugin.rb" should contain each of these content parts:
@@ -62,6 +64,7 @@ Feature: Adhearsion plugin generator
62
64
  | test_plugin/lib/test_plugin |
63
65
  | test_plugin/spec |
64
66
  And the following files should exist:
67
+ | test_plugin/.gitignore |
65
68
  | test_plugin/test_plugin.gemspec |
66
69
  | test_plugin/Rakefile |
67
70
  | test_plugin/README.md |
@@ -72,6 +75,7 @@ Feature: Adhearsion plugin generator
72
75
  | test_plugin/lib/test_plugin/controller_methods.rb |
73
76
  | test_plugin/spec/spec_helper.rb |
74
77
  | test_plugin/spec/test_plugin/controller_methods_spec.rb |
78
+ And the file "test_plugin/.gitignore" should contain "Gemfile.lock"
75
79
  And the file "test_plugin/test_plugin.gemspec" should contain "test_plugin/version"
76
80
  And the file "test_plugin/README.md" should contain "TestPlugin"
77
81
  And the file "test_plugin/lib/test_plugin.rb" should contain each of these content parts:
@@ -1,8 +1,15 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  Then /^the file "([^"]*)" should contain each of these content parts:$/ do |file, content_parts|
4
- parts = content_parts.split
4
+ parts = content_parts.split("\n")
5
5
  parts.each do |p|
6
6
  steps %Q{Then the file "#{file}" should contain "#{p}"}
7
7
  end
8
8
  end
9
+
10
+ Then /^the file "([^"]*)" should not contain each of these content parts:$/ do |file, content_parts|
11
+ parts = content_parts.split("\n")
12
+ parts.each do |p|
13
+ steps %Q{Then the file "#{file}" should not contain "#{p}"}
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- abort "ERROR: You are running Adhearsion on an unsupported version of Ruby (Ruby #{RUBY_VERSION} #{RUBY_RELEASE_DATE})! Please upgrade to at least Ruby v1.9.2, JRuby 1.7.0 or Rubinius 2.0." if RUBY_VERSION < "1.9.2"
3
+ abort "ERROR: You are running Adhearsion on an unsupported version of Ruby (Ruby #{RUBY_VERSION} #{RUBY_RELEASE_DATE})! Please upgrade to at least Ruby v1.9.3, JRuby 1.7.0 or Rubinius 2.0." if RUBY_VERSION < "1.9.3"
4
4
 
5
5
  %w{
6
6
  active_support/all
@@ -71,8 +71,7 @@ module Adhearsion
71
71
 
72
72
  def initialize_config
73
73
  _config = Configuration.new
74
- env = ENV['AHN_ENV'] || ENV['RAILS_ENV']
75
- env = env.to_sym if env.respond_to? :to_sym
74
+ env = environment.to_sym if environment.respond_to? :to_sym
76
75
  unless _config.valid_environment? env
77
76
  puts "You tried to initialize with an invalid environment name #{env}. Valid values are #{_config.valid_environments}."
78
77
  env = nil
@@ -81,6 +80,10 @@ module Adhearsion
81
80
  _config
82
81
  end
83
82
 
83
+ def environment
84
+ ENV['AHN_ENV'] || ENV['RAILS_ENV'] || :development
85
+ end
86
+
84
87
  def environments
85
88
  config.valid_environments
86
89
  end
@@ -17,7 +17,7 @@ module Adhearsion
17
17
  include Celluloid
18
18
  include HasGuardedHandlers
19
19
 
20
- execute_block_on_receiver :register_handler, :register_tmp_handler, :register_handler_with_priority, :register_event_handler, :on_joined, :on_unjoined, :on_end, :execute_controller
20
+ execute_block_on_receiver :register_handler, :register_tmp_handler, :register_handler_with_priority, :register_event_handler, :on_joined, :on_unjoined, :on_end, :execute_controller, *execute_block_on_receiver
21
21
  finalizer :finalize
22
22
 
23
23
  def self.new(*args, &block)
@@ -25,7 +25,7 @@ module Adhearsion
25
25
  def proxy.method_missing(*args)
26
26
  super
27
27
  rescue Celluloid::DeadActorError
28
- raise ExpiredError, "This call is expired and is no longer accessible"
28
+ raise ExpiredError, "This call is expired and is no longer accessible. See http://adhearsion.com/docs/calls for further details."
29
29
  end
30
30
  end
31
31
  end
@@ -33,6 +33,9 @@ module Adhearsion
33
33
  # @return [Symbol] the reason for the call ending
34
34
  attr_reader :end_reason
35
35
 
36
+ # @return [String] the reason code for the call ending
37
+ attr_reader :end_code
38
+
36
39
  # @return [Array<Adhearsion::CallController>] the set of call controllers executing on the call
37
40
  attr_reader :controllers
38
41
 
@@ -45,8 +48,25 @@ module Adhearsion
45
48
  # @return [Time] the time at which the call began. For inbound calls this is the time at which the call was offered to Adhearsion. For outbound calls it is the time at which the remote party answered.
46
49
  attr_reader :end_time
47
50
 
51
+ # @return [true, false] wether or not the call should be automatically hung up after executing its controller
52
+ attr_accessor :auto_hangup
53
+
48
54
  delegate :[], :[]=, :to => :variables
49
- delegate :to, :from, :to => :offer, :allow_nil => true
55
+
56
+ # @return [String] the value of the To header from the signaling protocol
57
+ delegate :to, to: :offer, allow_nil: true
58
+
59
+ # @return [String] the value of the From header from the signaling protocol
60
+ delegate :from, to: :offer, allow_nil: true
61
+
62
+ def self.uri(transport, id, domain)
63
+ return nil unless id
64
+ s = ""
65
+ s << transport << ":" if transport
66
+ s << id
67
+ s << "@" << domain if domain
68
+ s
69
+ end
50
70
 
51
71
  def initialize(offer = nil)
52
72
  register_initial_handlers
@@ -57,9 +77,11 @@ module Adhearsion
57
77
  @variables = HashWithIndifferentAccess.new
58
78
  @controllers = []
59
79
  @end_reason = nil
80
+ @end_code = nil
60
81
  @end_blocker = Celluloid::Condition.new
61
82
  @peers = {}
62
83
  @duration = nil
84
+ @auto_hangup = true
63
85
 
64
86
  self << offer if offer
65
87
  end
@@ -83,12 +105,7 @@ module Adhearsion
83
105
  # @return [String, nil] The uri at which the call resides
84
106
  #
85
107
  def uri
86
- return nil unless id
87
- s = ""
88
- s << transport << ":" if transport
89
- s << id
90
- s << "@" << domain if domain
91
- s
108
+ self.class.uri(transport, id, domain)
92
109
  end
93
110
 
94
111
  #
@@ -146,13 +163,26 @@ module Adhearsion
146
163
  end
147
164
  end
148
165
 
166
+ #
167
+ # Register a handler for events on this call. Note that Adhearsion::Call implements the has-guarded-handlers API, and all of its methods are available. Specifically, all Adhearsion events are available on the `:event` channel.
168
+ #
169
+ # @param [guards] guards take a look at the guards documentation
170
+ #
171
+ # @yield [Object] trigger_object the incoming event
172
+ #
173
+ # @return [String] handler ID for later manipulation
174
+ #
175
+ # @see http://adhearsion.github.io/has-guarded-handlers for more details
176
+ #
149
177
  def register_event_handler(*guards, &block)
150
178
  register_handler :event, *guards, &block
151
179
  end
152
180
 
153
181
  def deliver_message(message)
154
182
  logger.debug "Receiving message: #{message.inspect}"
155
- catching_standard_errors { trigger_handler :event, message }
183
+ catching_standard_errors do
184
+ trigger_handler :event, message, broadcast: true, exception_callback: ->(e) { Adhearsion::Events.trigger :exception, [e, logger] }
185
+ end
156
186
  end
157
187
  alias << deliver_message
158
188
 
@@ -166,12 +196,10 @@ module Adhearsion
166
196
  @offer = offer
167
197
  @client = offer.client
168
198
  @start_time = Time.now
169
- throw :pass
170
199
  end
171
200
 
172
201
  register_event_handler Punchblock::HasHeaders do |event|
173
202
  merge_headers event.headers
174
- throw :pass
175
203
  end
176
204
 
177
205
  on_joined do |event|
@@ -183,7 +211,8 @@ module Adhearsion
183
211
  type = :mixer
184
212
  end
185
213
  logger.info "Joined to #{type} #{target}"
186
- @peers[target] = Adhearsion.active_calls[target]
214
+ call = Adhearsion.active_calls.with_uri(target)
215
+ @peers[target] = call
187
216
  signal :joined, target
188
217
  end
189
218
 
@@ -206,10 +235,10 @@ module Adhearsion
206
235
  @duration = @end_time - @start_time if @start_time
207
236
  clear_from_active_calls
208
237
  @end_reason = event.reason
238
+ @end_code = event.platform_code
209
239
  @end_blocker.broadcast event.reason
210
240
  @commands.terminate
211
241
  after(Adhearsion.config.platform.after_hangup_lifetime) { terminate }
212
- throw :pass
213
242
  end
214
243
  end
215
244
 
@@ -234,7 +263,6 @@ module Adhearsion
234
263
  def on_joined(target = nil, &block)
235
264
  register_event_handler Punchblock::Event::Joined, *guards_for_target(target) do |event|
236
265
  block.call event
237
- throw :pass
238
266
  end
239
267
  end
240
268
 
@@ -246,22 +274,16 @@ module Adhearsion
246
274
  # @option target [String] mixer_name The mixer name to guard on
247
275
  #
248
276
  def on_unjoined(target = nil, &block)
249
- register_event_handler Punchblock::Event::Unjoined, *guards_for_target(target) do |event|
250
- block.call event
251
- throw :pass
252
- end
277
+ register_event_handler Punchblock::Event::Unjoined, *guards_for_target(target), &block
253
278
  end
254
279
 
255
280
  # @private
256
281
  def guards_for_target(target)
257
- target ? [join_options_with_target(target)] : []
282
+ target ? [target_from_join_options(join_options_with_target(target))] : []
258
283
  end
259
284
 
260
285
  def on_end(&block)
261
- register_event_handler Punchblock::Event::End do |event|
262
- block.call event
263
- throw :pass
264
- end
286
+ register_event_handler Punchblock::Event::End, &block
265
287
  end
266
288
 
267
289
  #
@@ -304,10 +326,29 @@ module Adhearsion
304
326
  # @option target [String] mixer_name The mixer to join to
305
327
  # @param [Hash, Optional] options further options to be joined with
306
328
  #
329
+ # @return [Hash] where :command is the issued command, :joined_waiter is a #wait responder which is triggered when the join is complete, and :unjoined_waiter is a #wait responder which is triggered when the entities are unjoined
330
+ #
307
331
  def join(target, options = {})
308
332
  logger.info "Joining to #{target}"
333
+
334
+ joined_condition = CountDownLatch.new(1)
335
+ on_joined target do
336
+ joined_condition.countdown!
337
+ end
338
+
339
+ unjoined_condition = CountDownLatch.new(1)
340
+ on_unjoined target do
341
+ unjoined_condition.countdown!
342
+ end
343
+
344
+ on_end do
345
+ joined_condition.countdown!
346
+ unjoined_condition.countdown!
347
+ end
348
+
309
349
  command = Punchblock::Command::Join.new options.merge(join_options_with_target(target))
310
350
  write_and_await_response command
351
+ {command: command, joined_condition: joined_condition, unjoined_condition: unjoined_condition}
311
352
  end
312
353
 
313
354
  ##
@@ -329,7 +370,7 @@ module Adhearsion
329
370
  when Call
330
371
  { :call_uri => target.uri }
331
372
  when String
332
- { :call_uri => "#{transport}:#{target}@#{domain}" }
373
+ { :call_uri => self.class.uri(transport, target, domain) }
333
374
  when Hash
334
375
  abort ArgumentError.new "You cannot specify both a call URI and mixer name" if target.has_key?(:call_uri) && target.has_key?(:mixer_name)
335
376
  target
@@ -338,6 +379,13 @@ module Adhearsion
338
379
  end
339
380
  end
340
381
 
382
+ # @private
383
+ def target_from_join_options(options)
384
+ call_uri = options[:call_uri]
385
+ return {call_uri: call_uri} if call_uri
386
+ {mixer_name: options[:mixer_name]}
387
+ end
388
+
341
389
  def wait_for_joined(expected_target)
342
390
  target = nil
343
391
  until target == expected_target do
@@ -361,25 +409,27 @@ module Adhearsion
361
409
  end
362
410
 
363
411
  # @private
364
- def write_and_await_response(command, timeout = 60)
412
+ def write_and_await_response(command, timeout = 60, fatal = false)
365
413
  @commands << command
366
414
  write_command command
367
415
 
416
+ error_handler = fatal ? ->(error) { raise error } : ->(error) { abort error }
417
+
368
418
  response = defer { command.response timeout }
369
419
  case response
370
420
  when Punchblock::ProtocolError
371
421
  if response.name == :item_not_found
372
- abort Hangup.new(@end_reason)
422
+ error_handler[Hangup.new(@end_reason)]
373
423
  else
374
- abort response
424
+ error_handler[response]
375
425
  end
376
426
  when Exception
377
- abort response
427
+ error_handler[response]
378
428
  end
379
429
 
380
430
  command
381
431
  rescue Timeout::Error
382
- abort CommandTimeout.new(command.to_s)
432
+ error_handler[CommandTimeout.new(command.to_s)]
383
433
  ensure
384
434
  @commands.delete command
385
435
  end
@@ -392,18 +442,39 @@ module Adhearsion
392
442
  client.execute_command command, call_id: id, domain: domain, async: true
393
443
  end
394
444
 
445
+ ##
446
+ # Sends a message to the caller
447
+ #
448
+ # @param [String] body The message text.
449
+ # @param [Hash, Optional] options The message options.
450
+ # @option options [String] subject The message subject.
451
+ #
452
+ def send_message(body, options = {})
453
+ logger.debug "Sending message: #{body}"
454
+ client.send_message id, domain, body, options
455
+ end
456
+
395
457
  # @private
396
458
  def logger_id
397
459
  "#{self.class}: #{id}@#{domain}"
398
460
  end
399
461
  # @private
400
462
  def inspect
463
+ return "..." if Celluloid.detect_recursion
401
464
  attrs = [:offer, :end_reason, :commands, :variables, :controllers, :to, :from].map do |attr|
402
465
  "#{attr}=#{send(attr).inspect}"
403
466
  end
404
467
  "#<#{self.class}:#{id}@#{domain} #{attrs.join ', '}>"
405
468
  end
406
469
 
470
+ #
471
+ # Execute a call controller asynchronously against this call.
472
+ #
473
+ # @param [Adhearsion::CallController] controller an instance of a controller initialized for this call
474
+ # @param [Proc] a callback to be executed when the controller finishes execution
475
+ #
476
+ # @yield execute the current block as the body of a controller by specifying no controller instance
477
+ #
407
478
  def execute_controller(controller = nil, completion_callback = nil, &block)
408
479
  raise ArgumentError, "Cannot supply a controller and a block at the same time" if controller && block_given?
409
480
  controller ||= CallController.new current_actor, &block