adhearsion 2.0.0.alpha1 → 2.0.0.alpha2

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