adhearsion 2.0.0.alpha2 → 2.0.0.alpha3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +16 -14
  2. data/CHANGELOG.md +17 -1
  3. data/adhearsion.gemspec +14 -13
  4. data/features/cli_basic.feature +30 -0
  5. data/features/cli_create.feature +24 -0
  6. data/features/cli_daemon.feature +21 -0
  7. data/features/cli_generate.feature +9 -0
  8. data/features/cli_start.feature +33 -0
  9. data/features/cli_stop.feature +38 -0
  10. data/features/controller_generator.feature +19 -0
  11. data/features/plugin_generator.feature +55 -0
  12. data/lib/adhearsion.rb +5 -1
  13. data/lib/adhearsion/call.rb +57 -51
  14. data/lib/adhearsion/call_controller.rb +4 -20
  15. data/lib/adhearsion/call_controller/dial.rb +34 -4
  16. data/lib/adhearsion/calls.rb +4 -1
  17. data/lib/adhearsion/cli_commands.rb +31 -6
  18. data/lib/adhearsion/configuration.rb +2 -6
  19. data/lib/adhearsion/console.rb +56 -17
  20. data/lib/adhearsion/generators.rb +53 -2
  21. data/lib/adhearsion/generators/app/app_generator.rb +2 -24
  22. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +1 -1
  23. data/lib/adhearsion/generators/controller/controller_generator.rb +18 -0
  24. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +4 -0
  25. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +3 -0
  26. data/lib/adhearsion/generators/generator.rb +77 -0
  27. data/lib/adhearsion/generators/plugin/plugin_generator.rb +33 -0
  28. data/lib/adhearsion/generators/plugin/templates/.gitignore +9 -0
  29. data/lib/adhearsion/generators/plugin/templates/Gemfile.tt +4 -0
  30. data/lib/adhearsion/generators/plugin/templates/README.md.tt +2 -0
  31. data/lib/adhearsion/generators/plugin/templates/Rakefile.tt +1 -0
  32. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template.rb.tt +5 -0
  33. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/controller_methods.rb.tt +10 -0
  34. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/plugin.rb.tt +29 -0
  35. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/version.rb.tt +3 -0
  36. data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +35 -0
  37. data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +26 -0
  38. data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +13 -0
  39. data/lib/adhearsion/initializer.rb +14 -22
  40. data/lib/adhearsion/logging.rb +25 -16
  41. data/lib/adhearsion/outbound_call.rb +3 -3
  42. data/lib/adhearsion/plugin.rb +14 -0
  43. data/lib/adhearsion/punchblock_plugin.rb +6 -1
  44. data/lib/adhearsion/punchblock_plugin/initializer.rb +8 -8
  45. data/lib/adhearsion/tasks/configuration.rb +1 -1
  46. data/lib/adhearsion/version.rb +1 -1
  47. data/spec/adhearsion/call_controller/dial_spec.rb +108 -17
  48. data/spec/adhearsion/call_controller_spec.rb +7 -64
  49. data/spec/adhearsion/call_spec.rb +48 -29
  50. data/spec/adhearsion/calls_spec.rb +7 -0
  51. data/spec/adhearsion/configuration_spec.rb +14 -14
  52. data/spec/adhearsion/console_spec.rb +124 -4
  53. data/spec/adhearsion/generators_spec.rb +17 -0
  54. data/spec/adhearsion/initializer_spec.rb +22 -18
  55. data/spec/adhearsion/logging_spec.rb +78 -48
  56. data/spec/adhearsion/outbound_call_spec.rb +6 -15
  57. data/spec/adhearsion/plugin_spec.rb +18 -0
  58. data/spec/adhearsion/process_spec.rb +10 -1
  59. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +8 -6
  60. data/spec/spec_helper.rb +5 -2
  61. data/spec/support/call_controller_test_helpers.rb +1 -1
  62. data/spec/support/initializer_stubs.rb +1 -1
  63. metadata +106 -66
  64. data/features/cli.feature +0 -108
  65. data/lib/adhearsion/initializer/logging.rb +0 -33
  66. data/spec/adhearsion/initializer/logging_spec.rb +0 -55
@@ -34,16 +34,13 @@ module Adhearsion
34
34
  end
35
35
 
36
36
  class << self
37
- def exec(controller, fresh_call = true)
38
- return unless controller
39
-
37
+ def exec(controller)
40
38
  new_controller = catch :pass_controller do
41
- controller.skip_accept! unless fresh_call
42
39
  controller.execute!
43
40
  nil
44
41
  end
45
42
 
46
- exec new_controller, false
43
+ exec new_controller if new_controller
47
44
  end
48
45
 
49
46
  ##
@@ -57,7 +54,7 @@ module Adhearsion
57
54
 
58
55
  delegate :[], :[]=, :to => :@metadata
59
56
  delegate :variables, :logger, :to => :call
60
- delegate :write_and_await_response, :accept, :answer, :reject, :mute, :unmute, :join, :to => :call
57
+ delegate :write_and_await_response, :answer, :reject, :mute, :unmute, :join, :to => :call
61
58
 
62
59
  def initialize(call, metadata = nil, &block)
63
60
  @call, @metadata, @block = call, metadata || {}, block
@@ -65,7 +62,6 @@ module Adhearsion
65
62
 
66
63
  def execute!(*options)
67
64
  execute_callbacks :before_call
68
- accept if auto_accept?
69
65
  run
70
66
  rescue Hangup
71
67
  logger.info "Call was hung up"
@@ -96,24 +92,12 @@ module Adhearsion
96
92
  end
97
93
  end
98
94
 
99
- def skip_accept!
100
- @skip_accept = true
101
- end
102
-
103
- def skip_accept?
104
- @skip_accept || false
105
- end
106
-
107
- def auto_accept?
108
- Adhearsion.config.platform.automatically_accept_incoming_calls && !skip_accept?
109
- end
110
-
111
95
  def after_call
112
96
  @after_call ||= execute_callbacks :after_call
113
97
  end
114
98
 
115
99
  def hangup(headers = nil)
116
- hangup_response = call.hangup! headers
100
+ hangup_response = call.hangup headers
117
101
  after_call unless hangup_response == false
118
102
  end
119
103
 
@@ -31,23 +31,43 @@ module Adhearsion
31
31
  # dial "IAX2/my.id@voipjet/19095551234", :from => "John Doe <9095551234>"
32
32
  #
33
33
  def dial(to, options = {}, latch = nil)
34
- latch ||= CountDownLatch.new 1
34
+ targets = Array(to)
35
+
36
+ latch ||= CountDownLatch.new targets.size
37
+
38
+ call.on_end { |_| latch.countdown! until latch.count == 0 }
35
39
 
36
40
  _for = options.delete :for
37
41
  options[:timeout] ||= _for if _for
38
42
 
39
- calls = Array(to).map do |target|
43
+ options[:from] ||= call.from
44
+
45
+ calls = targets.map do |target|
40
46
  new_call = OutboundCall.new options
41
47
 
42
48
  new_call.on_answer do |event|
43
49
  calls.each do |call_to_hangup, target|
44
- call_to_hangup.hangup! unless call_to_hangup.id == new_call.id
50
+ begin
51
+ next if call_to_hangup.id == new_call.id
52
+ logger.debug "Hanging up call #{call_to_hangup.id} because it was not the first to answer a #dial"
53
+ call_to_hangup.hangup
54
+ rescue Celluloid::DeadActorError
55
+ # This actor may previously have been shut down due to the call ending
56
+ end
57
+ end
58
+
59
+ new_call.register_event_handler Punchblock::Event::Unjoined, :other_call_id => call.id do |event|
60
+ new_call[:"dial_countdown_#{call.id}"] = true
61
+ latch.countdown!
62
+ throw :pass
45
63
  end
64
+
65
+ logger.debug "Joining call #{new_call.id} to #{call.id} due to a #dial"
46
66
  new_call.join call
47
67
  end
48
68
 
49
69
  new_call.on_end do |event|
50
- latch.countdown!
70
+ latch.countdown! unless new_call[:"dial_countdown_#{call.id}"]
51
71
  end
52
72
 
53
73
  [new_call, target]
@@ -60,6 +80,16 @@ module Adhearsion
60
80
 
61
81
  timeout = latch.wait options[:timeout]
62
82
 
83
+ logger.debug "#dial finished. Hanging up #{calls.size} outbound calls #{calls.inspect}."
84
+ calls.each do |outbound_call|
85
+ begin
86
+ logger.debug "Hanging up #{outbound_call} because the #dial that created it is complete."
87
+ outbound_call.hangup
88
+ rescue Celluloid::DeadActorError
89
+ # This actor may previously have been shut down due to the call ending
90
+ end
91
+ end
92
+
63
93
  return timeout unless timeout
64
94
 
65
95
  calls.size == 1 ? calls.first : calls
@@ -18,7 +18,10 @@ module Adhearsion
18
18
  end
19
19
 
20
20
  def <<(call)
21
- atomically { calls[call.id] = call }
21
+ atomically do
22
+ calls[call.id] = call
23
+ end
24
+ self
22
25
  end
23
26
 
24
27
  def any?
@@ -28,9 +28,22 @@ module Adhearsion
28
28
 
29
29
  desc "create /path/to/directory", "Create a new Adhearsion application under the given path"
30
30
  def create(path)
31
- require 'adhearsion/generators'
32
31
  require 'adhearsion/generators/app/app_generator'
33
- Adhearsion::Generators::AppGenerator.start
32
+ Generators::AppGenerator.start
33
+ end
34
+
35
+ desc "generate [generator_name] arguments", "Invoke a generator"
36
+ def generate(generator_name = nil, *args)
37
+ require 'adhearsion/generators/controller/controller_generator'
38
+ Generators.add_generator :controller, Adhearsion::Generators::ControllerGenerator
39
+ require 'adhearsion/generators/plugin/plugin_generator'
40
+ Generators.add_generator :plugin, Adhearsion::Generators::PluginGenerator
41
+
42
+ if generator_name
43
+ Generators.invoke generator_name
44
+ else
45
+ Generators.help
46
+ end
34
47
  end
35
48
 
36
49
  desc "version", "Shows Adhearsion version"
@@ -55,13 +68,16 @@ module Adhearsion
55
68
  def stop(path = nil)
56
69
  execute_from_app_dir! path
57
70
 
71
+ path ||= '.'
72
+
58
73
  pid_file = if options[:pidfile]
59
- File.expand_path File.exists?(File.expand_path(options[:pidfile])) ?
60
- options[:pidfile] :
61
- File.join(path, options[:pidfile])
74
+ File.exists?(File.expand_path(options[:pidfile])) ?
75
+ options[:pidfile] :
76
+ File.join(path, options[:pidfile])
62
77
  else
63
- path + '/adhearsion.pid'
78
+ File.join path, Adhearsion::Initializer::DEFAULT_PID_FILE_NAME
64
79
  end
80
+ pid_file = File.expand_path pid_file
65
81
 
66
82
  begin
67
83
  pid = File.read(pid_file).to_i
@@ -79,6 +95,8 @@ module Adhearsion
79
95
  ::Process.kill "KILL", pid
80
96
  rescue Errno::ESRCH
81
97
  end
98
+
99
+ File.delete pid_file
82
100
  end
83
101
 
84
102
  desc "restart </path/to/directory>", "Restart the Adhearsion server"
@@ -144,6 +162,13 @@ module Adhearsion
144
162
  end
145
163
  end
146
164
 
165
+ class UnknownGeneratorError < Thor::Error
166
+ def initialize(gentype)
167
+ puts "Please specify generator to use (#{Adhearsion::Generators.mappings.keys.join(", ")})"
168
+ super "Unknown command: #{gentype}"
169
+ end
170
+ end
171
+
147
172
  class PathInvalid < Thor::Error
148
173
  def initialize(path)
149
174
  super "Directory #{path} does not belong to an Adhearsion project!"
@@ -21,15 +21,11 @@ module Adhearsion
21
21
  root nil, :desc => "Adhearsion application root folder"
22
22
 
23
23
  lib "lib", :desc => <<-__
24
- Folder to include the own libraries to be used. Adhearsion loads any ruby file
25
- located into this folder during the bootstrap process. Set to nil if you do not
24
+ Folder to include the own libraries to be used. Adhearsion loads any ruby file
25
+ located into this folder during the bootstrap process. Set to nil if you do not
26
26
  want these files to be loaded. This folder is relative to the application root folder.
27
27
  __
28
28
 
29
- automatically_accept_incoming_calls true, :transform => Proc.new { |v| v == 'true' }, :desc => <<-__
30
- Adhearsion will accept automatically any inbound call
31
- __
32
-
33
29
  environment :development, :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
34
30
  Active environment. Supported values: development, production, staging, test
35
31
  __
@@ -2,9 +2,10 @@ require 'pry'
2
2
 
3
3
  module Adhearsion
4
4
  class Console
5
- include Adhearsion
6
5
  include Singleton
7
6
 
7
+ delegate :silence!, :unsilence!, :to => Adhearsion::Logging
8
+
8
9
  class << self
9
10
  ##
10
11
  # Include another external functionality into the console
@@ -17,6 +18,12 @@ module Adhearsion
17
18
  end
18
19
  end
19
20
 
21
+ attr_accessor :input
22
+
23
+ def initialize
24
+ @input = $stdin
25
+ end
26
+
20
27
  ##
21
28
  # Start the Adhearsion console
22
29
  #
@@ -48,21 +55,47 @@ module Adhearsion
48
55
  logger.info "Shutting down"
49
56
  end
50
57
 
58
+ def log_level(level = nil)
59
+ if level
60
+ Adhearsion::Logging.level = level
61
+ else
62
+ ::Logging::LEVELS.invert[Adhearsion::Logging.level].to_sym
63
+ end
64
+ end
65
+
66
+ def shutdown!
67
+ Process.shutdown!
68
+ end
69
+
51
70
  def calls
52
71
  Adhearsion.active_calls
53
72
  end
54
73
 
55
- def use(call)
56
- unless call.is_a? Adhearsion::Call
57
- raise ArgumentError unless Adhearsion.active_calls[call]
58
- call = Adhearsion.active_calls[call]
59
- end
60
- Pry.prompt = [ proc { "AHN<#{call.channel}> " },
61
- proc { "AHN<#{call.channel}? " } ]
62
-
63
- # Pause execution of the thread currently controlling the call
64
- call.with_command_lock do
65
- CallWrapper.new(call).pry
74
+ def take(call = nil)
75
+ case call
76
+ when Call
77
+ interact_with_call call
78
+ when String
79
+ if call = calls[call]
80
+ interact_with_call call
81
+ else
82
+ logger.error "An active call with that ID does not exist"
83
+ end
84
+ when nil
85
+ if calls.size == 1
86
+ interact_with_call calls.values.first
87
+ else
88
+ puts "Please choose a call:"
89
+ current_calls = calls.values
90
+ current_calls.each_with_index do |call, index|
91
+ puts "#{index}. (#{call.is_a?(OutboundCall) ? 'o' : 'i' }) #{call.id} from #{call.from} to #{call.to}"
92
+ end
93
+ index = input.gets.chomp.to_i
94
+ call = current_calls[index]
95
+ interact_with_call call
96
+ end
97
+ else
98
+ raise ArgumentError
66
99
  end
67
100
  end
68
101
 
@@ -76,12 +109,18 @@ module Adhearsion
76
109
  end
77
110
  end
78
111
 
79
- class CallWrapper
80
- attr_accessor :call
112
+ private
113
+
114
+ def interact_with_call(call)
115
+ Pry.prompt = [ proc { "AHN<#{call.id}> " },
116
+ proc { "AHN<#{call.id}? " } ]
117
+
118
+ CallController.exec InteractiveController.new(call)
119
+ end
81
120
 
82
- def initialize(call)
83
- @call = call
84
- extend Adhearsion::Commands.for('asterisk')
121
+ class InteractiveController < CallController
122
+ def run
123
+ pry
85
124
  end
86
125
  end
87
126
  end
@@ -1,5 +1,56 @@
1
1
  module Adhearsion
2
2
  module Generators
3
3
  extend ActiveSupport::Autoload
4
- end
5
- end
4
+
5
+ autoload :Generator
6
+
7
+ class << self
8
+
9
+ # Show help message with available generators.
10
+ def help(command = 'generate')
11
+ puts "Usage: ahn #{command} GENERATOR_NAME [args] [options]"
12
+ puts
13
+ puts "Please choose a generator below."
14
+ puts
15
+
16
+ mappings.each_pair do |name, klass|
17
+ puts name
18
+ end
19
+ end
20
+
21
+ def invoke(generator_name, args = ARGV)
22
+ klass = Generators.mappings[generator_name.to_sym]
23
+ raise UnknownGeneratorError, generator_name unless klass
24
+
25
+ args << "--help" if args.empty? && klass.arguments.any?(&:required?)
26
+
27
+ klass.start args
28
+ end
29
+
30
+ ##
31
+ # Return a ordered list of task with their class
32
+ #
33
+ def mappings
34
+ @_mappings ||= Hash.new
35
+ end
36
+
37
+ ##
38
+ # Globally add a new generator class to +ahn generate+
39
+ #
40
+ # @param [Symbol] name
41
+ # key name for generator mapping
42
+ # @param [Class] klass
43
+ # class of generator
44
+ #
45
+ # @return [Hash] generator mappings
46
+ #
47
+ # @example
48
+ # Adhearsion::Generators.add_generator :myplugin, MyPluginGenerator
49
+ #
50
+ def add_generator(name, klass)
51
+ mappings[name] = klass
52
+ end
53
+
54
+ end#class << self
55
+ end#module
56
+ end#module
@@ -1,33 +1,11 @@
1
- begin
2
- require 'thor/group'
3
- rescue LoadError
4
- puts "Thor is not available.\nIf you ran this command from a git checkout " \
5
- "of Adhearsion, please make sure thor is installed,\nand run this command " \
6
- "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`"
7
- exit
8
- end
9
-
10
1
  module Adhearsion
11
2
  module Generators
12
- class AppGenerator < Thor::Group
13
- include Thor::Actions
3
+ class AppGenerator < Generator
14
4
 
15
5
  BASEDIRS = %w( config lib script )
16
6
 
17
- argument :app_action, :type => :string
18
- argument :app_path, :type => :string
19
-
20
- def self.source_root(path = nil)
21
- path = File.join(base_root, 'templates')
22
- path if File.exists?(path)
23
- end
24
-
25
- def self.base_root
26
- File.dirname(__FILE__)
27
- end
28
-
29
7
  def setup_project
30
- self.destination_root = @app_path
8
+ self.destination_root = @generator_name
31
9
  BASEDIRS.each { |dir| directory dir }
32
10
  template "Gemfile.erb", "Gemfile"
33
11
  copy_file "gitignore", ".gitignore"
@@ -29,7 +29,7 @@ Adhearsion.config do |config|
29
29
  ##
30
30
  # Use with Asterisk
31
31
  #
32
- # config.punchblock.platform = :asterisk # Your AMI username
32
+ # config.punchblock.platform = :asterisk # Use Asterisk
33
33
  # config.punchblock.username = "" # Your AMI username
34
34
  # config.punchblock.password = "" # Your AMI password
35
35
  # config.punchblock.host = "127.0.0.1" # Your AMI host
@@ -0,0 +1,18 @@
1
+ module Adhearsion
2
+ module Generators
3
+ class ControllerGenerator < Generator
4
+
5
+ argument :controller_name, :type => :string
6
+
7
+ def create_controller
8
+ raise Exception, "Generator commands need to be run in an Adhearsion app directory" unless ScriptAhnLoader.in_ahn_application?('.')
9
+ self.destination_root = '.'
10
+ empty_directory 'lib'
11
+ empty_directory 'spec'
12
+ template 'lib/controller.rb', "lib/#{@controller_name.underscore}.rb"
13
+ template 'spec/controller_spec.rb', "spec/#{@controller_name.underscore}_spec.rb"
14
+ end
15
+
16
+ end
17
+ end
18
+ end