adhearsion 2.0.0.alpha1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG.md +17 -0
  4. data/adhearsion.gemspec +4 -3
  5. data/features/app_generator.feature +3 -1
  6. data/features/cli.feature +7 -7
  7. data/features/support/env.rb +46 -0
  8. data/lib/adhearsion.rb +1 -2
  9. data/lib/adhearsion/call.rb +59 -19
  10. data/lib/adhearsion/call_controller.rb +20 -24
  11. data/lib/adhearsion/call_controller/dial.rb +18 -18
  12. data/lib/adhearsion/cli_commands.rb +26 -9
  13. data/lib/adhearsion/configuration.rb +39 -10
  14. data/lib/adhearsion/console.rb +61 -42
  15. data/lib/adhearsion/foundation/libc.rb +13 -0
  16. data/lib/adhearsion/generators/app/app_generator.rb +4 -1
  17. data/lib/adhearsion/generators/app/templates/{Gemfile → Gemfile.erb} +1 -1
  18. data/lib/adhearsion/generators/app/templates/Rakefile +3 -22
  19. data/lib/adhearsion/generators/app/templates/gitignore +7 -0
  20. data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +1 -0
  21. data/lib/adhearsion/generators/app/templates/script/ahn +1 -0
  22. data/lib/adhearsion/initializer.rb +24 -12
  23. data/lib/adhearsion/linux_proc_name.rb +41 -0
  24. data/lib/adhearsion/outbound_call.rb +10 -5
  25. data/lib/adhearsion/plugin.rb +29 -132
  26. data/lib/adhearsion/process.rb +4 -1
  27. data/lib/adhearsion/punchblock_plugin.rb +14 -5
  28. data/lib/adhearsion/punchblock_plugin/initializer.rb +8 -1
  29. data/lib/adhearsion/router/route.rb +1 -3
  30. data/lib/adhearsion/tasks.rb +6 -12
  31. data/lib/adhearsion/tasks/configuration.rb +7 -24
  32. data/lib/adhearsion/tasks/environment.rb +12 -0
  33. data/lib/adhearsion/tasks/plugins.rb +9 -14
  34. data/lib/adhearsion/version.rb +1 -1
  35. data/spec/adhearsion/call_controller/dial_spec.rb +46 -22
  36. data/spec/adhearsion/call_controller_spec.rb +48 -13
  37. data/spec/adhearsion/call_spec.rb +144 -23
  38. data/spec/adhearsion/calls_spec.rb +8 -4
  39. data/spec/adhearsion/console_spec.rb +24 -0
  40. data/spec/adhearsion/initializer/logging_spec.rb +0 -3
  41. data/spec/adhearsion/initializer_spec.rb +52 -37
  42. data/spec/adhearsion/logging_spec.rb +0 -3
  43. data/spec/adhearsion/outbound_call_spec.rb +12 -2
  44. data/spec/adhearsion/plugin_spec.rb +74 -184
  45. data/spec/adhearsion/process_spec.rb +59 -26
  46. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +3 -4
  47. data/spec/adhearsion/punchblock_plugin_spec.rb +11 -0
  48. data/spec/adhearsion/router/route_spec.rb +37 -6
  49. data/spec/adhearsion_spec.rb +31 -8
  50. data/spec/spec_helper.rb +14 -0
  51. data/spec/support/call_controller_test_helpers.rb +2 -2
  52. data/spec/support/logging_helpers.rb +2 -0
  53. metadata +85 -68
  54. data/lib/adhearsion/dialplan_controller.rb +0 -9
  55. data/lib/adhearsion/foundation/synchronized_hash.rb +0 -93
  56. data/lib/adhearsion/plugin/methods_container.rb +0 -6
  57. data/spec/adhearsion/dialplan_controller_spec.rb +0 -26
@@ -2,6 +2,17 @@ require 'fileutils'
2
2
  require 'adhearsion/script_ahn_loader'
3
3
  require 'thor'
4
4
 
5
+ class Thor
6
+ class Task
7
+ protected
8
+
9
+ def sans_backtrace(backtrace, caller) #:nodoc:
10
+ saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) }
11
+ saned -= caller
12
+ end
13
+ end
14
+ end
15
+
5
16
  module Adhearsion
6
17
  module CLI
7
18
  class AhnCommand < Thor
@@ -42,7 +53,7 @@ module Adhearsion
42
53
  desc "stop </path/to/directory>", "Stop a running Adhearsion server"
43
54
  method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
44
55
  def stop(path = nil)
45
- execute_from_app_dir! path, ARGV
56
+ execute_from_app_dir! path
46
57
 
47
58
  pid_file = if options[:pidfile]
48
59
  File.expand_path File.exists?(File.expand_path(options[:pidfile])) ?
@@ -55,10 +66,10 @@ module Adhearsion
55
66
  begin
56
67
  pid = File.read(pid_file).to_i
57
68
  rescue
58
- raise CLIException, "Could not read pid file #{pid_file}"
69
+ raise PIDReadError, pid_file
59
70
  end
60
71
 
61
- raise CLIException, "Could not read pid" if pid.nil?
72
+ raise PIDReadError, pid_file if pid.nil?
62
73
 
63
74
  say "Stopping Adhearsion server at #{path} with pid #{pid}"
64
75
  waiting_timeout = Time.now + 15
@@ -73,7 +84,7 @@ module Adhearsion
73
84
  desc "restart </path/to/directory>", "Restart the Adhearsion server"
74
85
  method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
75
86
  def restart(path = nil)
76
- execute_from_app_dir! path, ARGV
87
+ execute_from_app_dir! path
77
88
  invoke :stop
78
89
  invoke :daemon
79
90
  end
@@ -81,12 +92,12 @@ module Adhearsion
81
92
  protected
82
93
 
83
94
  def start_app(path, mode, pid_file = nil)
84
- execute_from_app_dir! path, ARGV
85
- say "Starting Adhearsion server at #{path}"
95
+ execute_from_app_dir! path
96
+ say "Starting Adhearsion server at #{Dir.pwd}"
86
97
  Adhearsion::Initializer.start :mode => mode, :pid_file => pid_file
87
98
  end
88
99
 
89
- def execute_from_app_dir!(path, *args)
100
+ def execute_from_app_dir!(path)
90
101
  return if in_app? and running_script_ahn?
91
102
 
92
103
  path ||= '.' if in_app?
@@ -95,8 +106,8 @@ module Adhearsion
95
106
  raise PathInvalid, path unless ScriptAhnLoader.in_ahn_application?(path)
96
107
 
97
108
  Dir.chdir path do
98
- args.flatten!
99
- args[1] = '.'
109
+ args = ARGV.dup
110
+ args[1] = File.expand_path path
100
111
  ScriptAhnLoader.exec_script_ahn! args
101
112
  end
102
113
  end
@@ -138,5 +149,11 @@ module Adhearsion
138
149
  super "Directory #{path} does not belong to an Adhearsion project!"
139
150
  end
140
151
  end
152
+
153
+ class PIDReadError < Thor::Error
154
+ def initialize(path)
155
+ super "Could not read pid from the file #{path}"
156
+ end
157
+ end
141
158
  end
142
159
  end
@@ -14,22 +14,38 @@ module Adhearsion
14
14
  def initialize(&block)
15
15
  initialize_environments
16
16
 
17
+ Loquacious.env_config = true
18
+ Loquacious.env_prefix = "AHN"
19
+
17
20
  Loquacious::Configuration.for :platform do
18
21
  root nil, :desc => "Adhearsion application root folder"
22
+
19
23
  lib "lib", :desc => <<-__
20
- Folder to include the own libraries to be used. Adhearsion loads any ruby file located into this folder during the bootstrap
21
- process. Set to nil if you do not want these files to be loaded. This folder is relative to the application root folder.
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
+ want these files to be loaded. This folder is relative to the application root folder.
22
27
  __
23
- automatically_accept_incoming_calls true, :desc => "Adhearsion will accept automatically any inbound call"
24
28
 
25
- environment :development, :desc => "Active environment. Supported values: development, production, staging, test"
29
+ automatically_accept_incoming_calls true, :transform => Proc.new { |v| v == 'true' }, :desc => <<-__
30
+ Adhearsion will accept automatically any inbound call
31
+ __
32
+
33
+ environment :development, :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
34
+ Active environment. Supported values: development, production, staging, test
35
+ __
36
+
37
+ process_name "ahn", :desc => <<-__
38
+ Adhearsion process name, useful to make it easier to find in the process list
39
+ Pro tip: set this to your application's name and you can do "killall myapp"
40
+ Does not work under JRuby.
41
+ __
26
42
 
27
43
  desc "Log configuration"
28
44
  logging {
29
- level :info, :desc => <<-__
45
+ level :info, :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
30
46
  Supported levels (in increasing severity) -- :trace < :debug < :info < :warn < :error < :fatal
31
47
  __
32
- outputters ["log/adhearsion.log"], :desc => <<-__
48
+ outputters ["log/adhearsion.log"], :transform => Proc.new { |val| Array(val) }, :desc => <<-__
33
49
  An array of log outputters to use. The default is to log to stdout and log/adhearsion.log
34
50
  __
35
51
  formatter nil, :desc => <<-__
@@ -136,13 +152,26 @@ module Adhearsion
136
152
  else
137
153
  return "" if Loquacious::Configuration.for(name).nil?
138
154
 
155
+ if args[:show_values]
156
+ name_leader = " config.#{name}."
157
+ desc_leader = " # "
158
+ name_value_sep = " = "
159
+ title_leader = " "
160
+ else
161
+ name_leader = ""
162
+ desc_leader = "#"
163
+ name_value_sep = " => "
164
+ title_leader = ""
165
+ end
166
+
139
167
  config = Loquacious::Configuration.help_for name,
140
- :name_leader => "",
141
- :desc_leader => "# ",
168
+ :name_leader => name_leader,
169
+ :desc_leader => desc_leader,
142
170
  :colorize => true,
143
- :io => desc
171
+ :io => desc,
172
+ :name_value_sep => name_value_sep
144
173
  config.show :values => args[:show_values]
145
- "\n# ******* Configuration for #{name} **************\n\n#{desc.string}"
174
+ "#{title_leader}# ******* Configuration for #{name} **************\n\n#{desc.string}"
146
175
  end
147
176
  end
148
177
  end
@@ -1,59 +1,78 @@
1
1
  require 'pry'
2
2
 
3
3
  module Adhearsion
4
- module Console
4
+ class Console
5
5
  include Adhearsion
6
+ include Singleton
6
7
 
7
8
  class << self
8
9
  ##
9
- # Start the Adhearsion console
10
- #
11
- def run
12
- Pry.prompt = [
13
- proc do |*args|
14
- obj, nest_level, pry_instance = args
15
- "AHN#{' ' * nest_level}> "
16
- end,
17
- proc do |*args|
18
- obj, nest_level, pry_instance = args
19
- "AHN#{' ' * nest_level}? "
20
- end
21
- ]
22
- Pry.config.command_prefix = "%"
23
- if libedit?
24
- logger.error "Cannot start. You are running Adhearsion on Ruby with libedit. You must use readline for the console to work."
25
- else
26
- logger.info "Starting up..."
27
- pry
28
- end
10
+ # Include another external functionality into the console
11
+ def mixin(mod)
12
+ include mod
29
13
  end
30
14
 
31
- def calls
32
- Adhearsion.active_calls
15
+ def method_missing(method, *args, &block)
16
+ instance.send method, *args, &block
33
17
  end
18
+ end
19
+
20
+ ##
21
+ # Start the Adhearsion console
22
+ #
23
+ def run
24
+ Pry.prompt = [
25
+ proc do |*args|
26
+ obj, nest_level, pry_instance = args
27
+ "AHN#{' ' * nest_level}> "
28
+ end,
29
+ proc do |*args|
30
+ obj, nest_level, pry_instance = args
31
+ "AHN#{' ' * nest_level}? "
32
+ end
33
+ ]
34
+ Pry.config.command_prefix = "%"
35
+ if libedit?
36
+ logger.error "Cannot start. You are running Adhearsion on Ruby with libedit. You must use readline for the console to work."
37
+ else
38
+ logger.info "Starting up..."
39
+ @pry_thread = Thread.current
40
+ binding.pry
41
+ end
42
+ end
43
+
44
+ def stop
45
+ return unless @pry_thread
46
+ @pry_thread.kill
47
+ @pry_thread = nil
48
+ logger.info "Shutting down"
49
+ end
34
50
 
35
- def use(call)
36
- unless call.is_a? Adhearsion::Call
37
- raise ArgumentError unless Adhearsion.active_calls[call]
38
- call = Adhearsion.active_calls[call]
39
- end
40
- Pry.prompt = [ proc { "AHN<#{call.channel}> " },
41
- proc { "AHN<#{call.channel}? " } ]
51
+ def calls
52
+ Adhearsion.active_calls
53
+ end
42
54
 
43
- # Pause execution of the thread currently controlling the call
44
- call.with_command_lock do
45
- CallWrapper.new(call).pry
46
- end
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]
47
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
66
+ end
67
+ end
48
68
 
49
- def libedit?
50
- begin
51
- # If NotImplemented then this might be libedit
52
- Readline.emacs_editing_mode
53
- false
54
- rescue NotImplementedError
55
- true
56
- end
69
+ def libedit?
70
+ begin
71
+ # If NotImplemented then this might be libedit
72
+ Readline.emacs_editing_mode
73
+ false
74
+ rescue NotImplementedError
75
+ true
57
76
  end
58
77
  end
59
78
 
@@ -0,0 +1,13 @@
1
+ require 'adhearsion/linux_proc_name'
2
+ require 'ffi'
3
+
4
+ module LibC
5
+ extend FFI::Library
6
+ ffi_lib FFI::Library::LIBC
7
+
8
+ begin
9
+ attach_function :prctl, [ :ulong, :ulong, :ulong, :ulong ], :int
10
+ rescue FFI::NotFoundError => ex
11
+ Adhearsion::LinuxProcName.error = "Error while attaching libc function prctl: #{ex}"
12
+ end
13
+ end
@@ -29,9 +29,12 @@ module Adhearsion
29
29
  def setup_project
30
30
  self.destination_root = @app_path
31
31
  BASEDIRS.each { |dir| directory dir }
32
- copy_file "Gemfile"
32
+ template "Gemfile.erb", "Gemfile"
33
+ copy_file "gitignore", ".gitignore"
34
+ copy_file "Procfile"
33
35
  copy_file "Rakefile"
34
36
  copy_file "README.md"
37
+ chmod "script/ahn", 0755
35
38
  end
36
39
  end
37
40
  end
@@ -1,6 +1,6 @@
1
1
  source :rubygems
2
2
 
3
- gem "adhearsion", :git => 'git://github.com/adhearsion/adhearsion.git', :branch => :develop
3
+ gem "adhearsion", ">=<%= Adhearsion::VERSION %>"
4
4
 
5
5
  #
6
6
  # Here are some example plugins you might like to use. Simply
@@ -1,27 +1,8 @@
1
- # This file is for the "rake" tool which automates project-related tasks. If you need to automate things, you can create
2
- # a new Rake task here. See http://rake.rubyforge.org for more info.
1
+ #!/usr/bin/env rake
2
+
3
3
  require 'rubygems'
4
4
  require 'bundler'
5
5
  Bundler.setup
6
6
  Bundler.require
7
7
 
8
- begin
9
- require 'adhearsion/tasks'
10
- rescue LoadError
11
- STDERR.puts "\nCannot load Adhearsion! Not all Rake tasks will be loaded!\n\n"
12
- end
13
-
14
- desc "Writes a .gitignore file that ignores certain SCM annoyances such as log files"
15
- task :gitignore do
16
- ignore_file = "#{Dir.pwd}/.gitignore"
17
- if File.exists? ignore_file
18
- STDERR.puts "File #{ignore_file} already exists!"
19
- else
20
- File.open ignore_file, 'w' do |file|
21
- # Add other files to the Array below
22
- %w[ log ].each do |pattern|
23
- file.puts pattern
24
- end
25
- end
26
- end
27
- end
8
+ require 'adhearsion/tasks'
@@ -0,0 +1,7 @@
1
+ .bundle
2
+ .*.swp
3
+ .*.swo
4
+ .DS_STORE
5
+ log/*.log
6
+ tmp
7
+ vendor/ruby
@@ -1,5 +1,6 @@
1
1
  class SimonGame < Adhearsion::CallController
2
2
  def run
3
+ answer
3
4
  reset
4
5
  loop do
5
6
  say_number
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  # This command will automatically be run when you run "ahn" with the Adhearsion gem installed from the root of your application.
2
3
 
3
4
  require File.expand_path('../../config/environment', __FILE__)
@@ -1,4 +1,5 @@
1
1
  require 'adhearsion/punchblock_plugin'
2
+ require 'adhearsion/linux_proc_name'
2
3
 
3
4
  module Adhearsion
4
5
  class Initializer
@@ -38,14 +39,14 @@ module Adhearsion
38
39
  def start
39
40
  resolve_pid_file_path
40
41
  load_lib_folder
41
- load_plugins_methods
42
42
  load_config
43
43
  initialize_log_paths
44
44
  daemonize! if should_daemonize?
45
+ start_logging
45
46
  launch_console if need_console?
46
47
  catch_termination_signal
47
48
  create_pid_file
48
- start_logging
49
+ set_ahn_proc_name
49
50
  logger.info "Loaded config in <#{Adhearsion.config.platform.environment}> environment"
50
51
  initialize_exception_logger
51
52
  update_rails_env_var
@@ -54,7 +55,11 @@ module Adhearsion
54
55
  logger.info "Adhearsion v#{Adhearsion::VERSION} initialized!"
55
56
  Adhearsion::Process.booted
56
57
 
58
+ run_plugins
57
59
  trigger_after_initialized_hooks
60
+
61
+ # This method will block until all important threads have finished.
62
+ # When it does, the process will exit.
58
63
  join_important_threads
59
64
  self
60
65
  end
@@ -62,20 +67,20 @@ module Adhearsion
62
67
  def update_rails_env_var
63
68
  env = ENV['AHN_ENV']
64
69
  if env && Adhearsion.config.valid_environment?(env.to_sym)
65
- if ENV['RAILS_ENV'] != env
70
+ if ENV['RAILS_ENV'] == env
71
+ logger.info "Using the configured value for RAILS_ENV : <#{env}>"
72
+ else
66
73
  logger.warn "Updating AHN_RAILS variable to <#{env}>"
67
74
  ENV['RAILS_ENV'] = env
68
- else
69
- logger.info "Using the configured value for RAILS_ENV : <#{env}>"
70
75
  end
71
76
  else
72
77
  env = ENV['RAILS_ENV']
73
- unless env
78
+ if env
79
+ logger.info "Using the configured value for RAILS_ENV : <#{env}>"
80
+ else
74
81
  env = Adhearsion.config.platform.environment.to_s
75
82
  logger.info "Defining AHN_RAILS variable to <#{env}>"
76
83
  ENV['RAILS_ENV'] = env
77
- else
78
- logger.info "Using the configured value for RAILS_ENV : <#{env}>"
79
84
  end
80
85
  end
81
86
  env
@@ -107,15 +112,18 @@ module Adhearsion
107
112
  def catch_termination_signal
108
113
  %w'INT TERM'.each do |process_signal|
109
114
  trap process_signal do
115
+ logger.info "Received #{process_signal} signal. Shutting down."
110
116
  Adhearsion::Process.shutdown
111
117
  end
112
118
  end
113
119
 
114
120
  trap 'QUIT' do
121
+ logger.info "Received QUIT signal. Hard shutting down."
115
122
  Adhearsion::Process.hard_shutdown
116
123
  end
117
124
 
118
125
  trap 'ABRT' do
126
+ logger.info "Received ABRT signal. Forcing stop."
119
127
  Adhearsion::Process.force_stop
120
128
  end
121
129
  end
@@ -177,14 +185,14 @@ module Adhearsion
177
185
  end
178
186
  end
179
187
 
180
- def load_plugins_methods
181
- Plugin.load_methods
182
- end
183
-
184
188
  def init_plugins
185
189
  Plugin.init_plugins
186
190
  end
187
191
 
192
+ def run_plugins
193
+ Plugin.run_plugins
194
+ end
195
+
188
196
  def should_daemonize?
189
197
  @mode == :daemon
190
198
  end
@@ -251,6 +259,10 @@ module Adhearsion
251
259
  end
252
260
  end
253
261
 
262
+ def set_ahn_proc_name
263
+ Adhearsion::LinuxProcName.set_proc_name Adhearsion.config.platform.process_name
264
+ end
265
+
254
266
  def trigger_after_initialized_hooks
255
267
  Events.trigger_immediately :after_initialized
256
268
  end