adhearsion 2.2.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14d65bdfc20111980b8445acf33c5a6d18ba166a
4
+ data.tar.gz: c10a07432dec071a4cdcf8e508517c4e2717cf62
5
+ SHA512:
6
+ metadata.gz: 2ecd96983d91e8e5da635158cb07537b18179958ea3e148c37086d4ab8e8b0099b3237407bae8aa33d644bef1ae0db7592ea083be58a7296bd7963716505131b
7
+ data.tar.gz: 3d2f040a64a072c7271690b5582e476b35e7449d7ce92be633e06bf0beb554853333e684efaec6db371a4a786eda5cb70f9f0abb6b47d42c869b81c2e86114dc
@@ -9,6 +9,7 @@ rvm:
9
9
  matrix:
10
10
  allow_failures:
11
11
  - rvm: rbx-19mode
12
+ - rvm: ruby-head
12
13
  env: ARUBA_TIMEOUT=120 RAILS_ENV=development AHN_ENV=development
13
14
  notifications:
14
15
  irc: "irc.freenode.org#adhearsion"
@@ -1,5 +1,19 @@
1
1
  # [develop](https://github.com/adhearsion/adhearsion)
2
2
 
3
+ # [2.3.0](https://github.com/adhearsion/adhearsion/compare/v2.2.1...v2.3.0) - [2013-03-25](https://rubygems.org/gems/adhearsion/versions/2.3.0)
4
+ * Feature: Allow specifying a renderer per invocation of `#menu`, `#interruptible_play`, `#ask`, `#play` and `#play!`
5
+
6
+ ```ruby
7
+ play 'tt-monkeys', renderer: :native
8
+ ```
9
+
10
+ * Feature: Make it possible to disable punchblock connection by way of settings
11
+ * Feature: Allow specifying exact input digits to recognize for interrupting a recording
12
+ * Bugfix: Run input validations before length check so we always return the more appropriate response
13
+ * Bugfix: Plugin initializers now run in the context of the plugin class, not nil
14
+ * Bugfix: Write PID without race conditions (#266)
15
+ * CS: Prevent Celluloid deprecation warnings
16
+
3
17
  # [2.2.1](https://github.com/adhearsion/adhearsion/compare/v2.2.0...v2.2.1) - [2013-01-06](https://rubygems.org/gems/adhearsion/versions/2.2.1)
4
18
  * Bugfix: No longer crash logging randomly
5
19
  * Bugfix: Correctly route outbound calls
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
@@ -17,7 +17,7 @@ Adhearsion rests above a lower-level telephony platform, for example [Asterisk](
17
17
 
18
18
  ## Requirements
19
19
 
20
- * Ruby 1.9.2+ or JRuby 1.6.7+
20
+ * Ruby 1.9.2+ or JRuby 1.7.0+
21
21
  * A VoIP platform:
22
22
  * Asterisk 1.8+
23
23
  * FreeSWITCH
@@ -61,7 +61,7 @@ Original author: [Jay Phillips](https://github.com/jicksta)
61
61
 
62
62
  ### Contributions
63
63
 
64
- Adhearsion has a set of [contribution guidelines](https://github.com/adhearsion/adhearsion/wiki/Contributing) which help to smooth the contribution process.
64
+ Adhearsion has a set of [contribution guidelines](http://adhearsion.com/docs/contributing) which help to smooth the contribution process.
65
65
  There is a pre-commit hook that runs encoding checks available in pre-commit. To use it, please copy it to .git/hooks/pre-commit and make it executable.
66
66
 
67
67
  ### Copyright
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_runtime_dependency 'activesupport', ["~> 3.0"]
21
21
  s.add_runtime_dependency 'adhearsion-loquacious', ["~> 1.9"]
22
22
  s.add_runtime_dependency 'bundler', ["~> 1.0"]
23
- s.add_runtime_dependency 'celluloid', ["~> 0.12", ">= 0.12.1"]
23
+ s.add_runtime_dependency 'celluloid', ["~> 0.13"]
24
24
  s.add_runtime_dependency 'countdownlatch'
25
25
  s.add_runtime_dependency 'deep_merge'
26
26
  s.add_runtime_dependency 'ffi', ["~> 1.0"]
@@ -42,7 +42,7 @@ Feature: Adhearsion App Generator
42
42
  And the file "Rakefile" should contain "adhearsion/tasks"
43
43
  And the file "Gemfile" should contain each of these content parts:
44
44
  """
45
- source :rubygems
45
+ source 'https://rubygems.org
46
46
  gem 'adhearsion-asterisk'
47
47
  """
48
48
  And the file "lib/simon_game.rb" should contain "class SimonGame"
@@ -135,7 +135,7 @@ module Adhearsion
135
135
  clear_from_active_calls
136
136
  @end_reason = event.reason
137
137
  commands.terminate
138
- after(Adhearsion.config.platform.after_hangup_lifetime) { current_actor.terminate! }
138
+ after(Adhearsion.config.platform.after_hangup_lifetime) { terminate }
139
139
  throw :pass
140
140
  end
141
141
  end
@@ -101,7 +101,7 @@ module Adhearsion
101
101
 
102
102
  # @private
103
103
  def execute!(*options)
104
- call.register_controller! self
104
+ call.async.register_controller self
105
105
  execute_callbacks :before_call
106
106
  run
107
107
  rescue Call::Hangup
@@ -125,6 +125,7 @@ module Adhearsion
125
125
  #
126
126
  # @param [Class] controller_class The class of controller to execute
127
127
  # @param [Hash] metadata generic key-value storage applicable to the controller
128
+ # @return The return value of the controller's run method
128
129
  #
129
130
  def invoke(controller_class, metadata = nil)
130
131
  controller = controller_class.new call, metadata
@@ -188,9 +188,9 @@ module Adhearsion
188
188
  digit = nil
189
189
  if sound_files.any? && menu_instance.digit_buffer_empty?
190
190
  if menu_instance.interruptible
191
- digit = interruptible_play(*sound_files)
191
+ digit = interruptible_play(*sound_files, renderer: menu_instance.renderer)
192
192
  else
193
- play(*sound_files)
193
+ play(*sound_files, renderer: menu_instance.renderer)
194
194
  end
195
195
  end
196
196
  digit || wait_for_digit(menu_instance.timeout)
@@ -11,7 +11,7 @@ module Adhearsion
11
11
 
12
12
  InvalidStructureError = Class.new Adhearsion::Error
13
13
 
14
- attr_reader :builder, :timeout, :tries_count, :max_number_of_tries, :terminator, :limit, :interruptible, :status
14
+ attr_reader :builder, :timeout, :tries_count, :max_number_of_tries, :terminator, :limit, :interruptible, :status, :renderer
15
15
 
16
16
  def initialize(options = {}, &block)
17
17
  @tries_count = 0 # Counts the number of tries the menu's been executed
@@ -22,6 +22,7 @@ module Adhearsion
22
22
  @interruptible = options.has_key?(:interruptible) ? options[:interruptible] : true
23
23
  @builder = MenuDSL::MenuBuilder.new
24
24
  @terminated = false
25
+ @renderer = options[:renderer]
25
26
 
26
27
  @builder.build(&block) if block
27
28
 
@@ -62,9 +63,8 @@ module Adhearsion
62
63
  return get_another_digit_or_timeout! if digit_buffer_empty?
63
64
 
64
65
  return menu_terminated! if @terminated
65
- return menu_limit_reached! if limit && digit_buffer.size >= limit
66
-
67
66
  return menu_validator_terminated! if execute_validator_hook
67
+ return menu_limit_reached! if limit && digit_buffer.size >= limit
68
68
 
69
69
  calculated_matches = builder.calculate_matches_for digit_buffer_string
70
70
 
@@ -61,7 +61,8 @@ module Adhearsion
61
61
  # @raises [PlaybackError] if (one of) the given argument(s) could not be played
62
62
  #
63
63
  def play(*arguments)
64
- player.play_ssml output_formatter.ssml_for_collection(arguments)
64
+ options = process_output_options arguments
65
+ player.play_ssml output_formatter.ssml_for_collection(arguments), options
65
66
  true
66
67
  end
67
68
 
@@ -89,7 +90,8 @@ module Adhearsion
89
90
  # @returns [Punchblock::Component::Output]
90
91
  #
91
92
  def play!(*arguments)
92
- async_player.play_ssml output_formatter.ssml_for_collection(arguments)
93
+ options = process_output_options arguments
94
+ async_player.play_ssml output_formatter.ssml_for_collection(arguments), options
93
95
  end
94
96
 
95
97
  #
@@ -98,13 +100,15 @@ module Adhearsion
98
100
  # The Punchblock backend will have to handle cases like Asterisk where there is a fixed sounds directory.
99
101
  #
100
102
  # @param [String] file http:// URL or full disk path to the sound file
101
- # @param [Hash] options Additional options to specify how exactly to say time specified.
103
+ # @param [Hash] options Additional options
102
104
  # @option options [String] :fallback The text to play if the file is not available
105
+ # @option options [Symbol] :renderer The media engine to use for rendering the file
103
106
  #
104
107
  # @raises [PlaybackError] if (one of) the given argument(s) could not be played
105
108
  #
106
- def play_audio(file, options = nil)
107
- player.play_ssml output_formatter.ssml_for_audio(file, options)
109
+ def play_audio(file, options = {})
110
+ renderer = options.delete :renderer
111
+ player.play_ssml(output_formatter.ssml_for_audio(file, options), renderer: renderer)
108
112
  true
109
113
  end
110
114
 
@@ -120,8 +124,9 @@ module Adhearsion
120
124
  # @raises [PlaybackError] if (one of) the given argument(s) could not be played
121
125
  # @returns [Punchblock::Component::Output]
122
126
  #
123
- def play_audio!(file, options = nil)
124
- async_player.play_ssml output_formatter.ssml_for_audio(file, options)
127
+ def play_audio!(file, options = {})
128
+ renderer = options.delete :renderer
129
+ async_player.play_ssml(output_formatter.ssml_for_audio(file, options), renderer: renderer)
125
130
  end
126
131
 
127
132
  #
@@ -214,8 +219,9 @@ module Adhearsion
214
219
  # @raises [PlaybackError] if (one of) the given argument(s) could not be played
215
220
  #
216
221
  def interruptible_play(*outputs)
222
+ options = process_output_options outputs
217
223
  outputs.find do |output|
218
- digit = stream_file output
224
+ digit = stream_file output, '0123456789#*', options
219
225
  return digit if digit
220
226
  end
221
227
  end
@@ -229,14 +235,14 @@ module Adhearsion
229
235
  # @return [String, nil] The pressed digit, or nil if nothing was pressed
230
236
  # @private
231
237
  #
232
- def stream_file(argument, digits = '0123456789#*')
238
+ def stream_file(argument, digits = '0123456789#*', output_options = {})
233
239
  result = nil
234
240
  stopper = Punchblock::Component::Input.new :mode => :dtmf,
235
241
  :grammar => {
236
242
  :value => grammar_accept(digits)
237
243
  }
238
244
 
239
- player.output output_formatter.ssml_for(argument) do |output_component|
245
+ player.output output_formatter.ssml_for(argument), output_options do |output_component|
240
246
  stopper.register_event_handler Punchblock::Event::Complete do |event|
241
247
  output_component.stop! unless output_component.complete?
242
248
  end
@@ -259,6 +265,11 @@ module Adhearsion
259
265
  @async_player ||= AsyncPlayer.new(self)
260
266
  end
261
267
 
268
+ # @private
269
+ def process_output_options(arguments)
270
+ arguments.last.is_a?(Hash) && arguments.count > 1 ? arguments.pop : {}
271
+ end
272
+
262
273
  #
263
274
  # @return [Formatter] an output formatter for the preparation of SSML documents for submission to the engine
264
275
  #
@@ -29,7 +29,8 @@ module Adhearsion
29
29
 
30
30
  @async = options.delete :async
31
31
 
32
- @stopper_component = options.delete(:interruptible) ? setup_stopper : nil
32
+ interruptible = options.delete :interruptible
33
+ @stopper_component = interruptible ? setup_stopper(interruptible) : nil
33
34
  @record_component = Punchblock::Component::Record.new options
34
35
  end
35
36
 
@@ -56,10 +57,11 @@ module Adhearsion
56
57
 
57
58
  private
58
59
 
59
- def setup_stopper
60
+ def setup_stopper(interrupt_digits)
61
+ interrupt_digits = interrupt_digits == true ? '0123456789#*' : interrupt_digits.to_s
60
62
  @stopper_component = Punchblock::Component::Input.new :mode => :dtmf,
61
63
  :grammar => {
62
- :value => @controller.grammar_accept('0123456789#*')
64
+ :value => @controller.grammar_accept(interrupt_digits)
63
65
  }
64
66
  @stopper_component.register_event_handler Punchblock::Event::Complete do |event|
65
67
  @record_component.stop! unless @record_component.complete?
@@ -112,7 +114,7 @@ module Adhearsion
112
114
  # @option options [String, Optional] :format File format used during recording.
113
115
  # @option options [String, Optional] :initial_timeout Controls how long (seconds) the recognizer should wait after the end of the prompt for the caller to speak before sending a Recorder event.
114
116
  # @option options [String, Optional] :final_timeout Controls the length (seconds) of a period of silence after callers have spoken to conclude they finished.
115
- # @option options [Boolean, Optional] :interruptible Allows the recording to be terminated by any single DTMF key, default is false
117
+ # @option options [Boolean, String, Optional] :interruptible Allows the recording to be terminated by any single DTMF key, default is false
116
118
  #
117
119
  # @return Punchblock::Component::Record
118
120
  #
@@ -8,7 +8,7 @@ module Adhearsion
8
8
 
9
9
  # Try to fork if at all possible retrying every 5 sec if the
10
10
  # maximum process limit for the system has been reached
11
- def safefork
11
+ def self.safefork
12
12
  begin
13
13
  pid = fork
14
14
  return pid if pid
@@ -19,18 +19,25 @@ module Adhearsion
19
19
  end
20
20
 
21
21
  # This method causes the current running process to become a daemon
22
- def daemonize(log_file = '/dev/null')
23
- oldmode = 0
22
+ def self.daemonize(log_file = '/dev/null')
24
23
  srand # Split rand streams between spawning and daemonized process
25
- safefork and exit # Fork and exit from the parent
24
+
25
+ # Fork, then exit when the child has exited below
26
+ if pid = safefork
27
+ ::Process.wait pid
28
+ exit
29
+ end
26
30
 
27
31
  # Detach from the controlling terminal
28
32
  raise 'Cannot detach from controlled terminal' unless sess_id = ::Process.setsid
29
33
 
30
34
  # Prevent the possibility of acquiring a controlling terminal
31
- if oldmode.zero?
32
- trap 'SIGHUP', 'IGNORE'
33
- exit if safefork
35
+ # Fork again, allow a PID file to be written, then exit
36
+ trap 'SIGHUP', 'IGNORE'
37
+
38
+ if pid = safefork
39
+ yield pid if block_given?
40
+ exit
34
41
  end
35
42
 
36
43
  Dir.chdir "/" # Release old working directory
@@ -39,7 +46,7 @@ module Adhearsion
39
46
  STDIN.reopen "/dev/null"
40
47
  STDOUT.reopen '/dev/null', "a"
41
48
  STDERR.reopen log_file, "a"
42
- oldmode ? sess_id : 0
49
+ return sess_id
43
50
  end
44
51
  end
45
52
  end
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gem "adhearsion", "~> <%= Adhearsion::VERSION.split('.')[0,2].join('.') %>"
4
4
 
@@ -42,13 +42,18 @@ module Adhearsion
42
42
  load_lib_folder
43
43
  load_config_file
44
44
  initialize_log_paths
45
- daemonize! if should_daemonize?
45
+
46
+ if should_daemonize?
47
+ daemonize!
48
+ else
49
+ create_pid_file
50
+ end
51
+
46
52
  Adhearsion.statistics
47
53
  start_logging
48
54
  debugging_log
49
55
  launch_console if need_console?
50
56
  catch_termination_signal
51
- create_pid_file
52
57
  set_ahn_proc_name
53
58
  initialize_exception_logger
54
59
  update_rails_env_var
@@ -218,9 +223,9 @@ module Adhearsion
218
223
 
219
224
  def daemonize!
220
225
  logger.info "Daemonizing now!"
221
- logger.debug "Creating PID file #{pid_file}"
222
- extend Adhearsion::CustomDaemonizer
223
- daemonize resolve_log_file_path
226
+ Adhearsion::CustomDaemonizer.daemonize resolve_log_file_path do |pid|
227
+ create_pid_file pid
228
+ end
224
229
  end
225
230
 
226
231
  def launch_console
@@ -261,11 +266,13 @@ module Adhearsion
261
266
  end
262
267
  end
263
268
 
264
- def create_pid_file
269
+ def create_pid_file(pid = nil)
265
270
  return unless pid_file
266
271
 
272
+ logger.debug "Creating PID file #{pid_file}"
273
+
267
274
  File.open pid_file, 'w' do |file|
268
- file.puts ::Process.pid
275
+ file.puts pid || ::Process.pid
269
276
  end
270
277
 
271
278
  Events.register_callback :shutdown do
@@ -188,11 +188,11 @@ module Adhearsion
188
188
  # @param opts Hash
189
189
  # * :before specify the plugin to be loaded before another plugin
190
190
  # * :after specify the plugin to be loaded after another plugin
191
- def init(name = nil, opts = {})
191
+ def init(name = nil, opts = {}, &block)
192
192
  name = plugin_name unless name
193
193
  block_given? or raise ArgumentError, "A block must be passed while defining the Plugin initialization process"
194
194
  opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
195
- Adhearsion::Plugin.initializers << Initializer.new(name, nil, opts, &Proc.new)
195
+ Adhearsion::Plugin.initializers << Initializer.new(name, self, opts, &block)
196
196
  end
197
197
 
198
198
  # Class method that will be used by subclasses to run the plugin
@@ -200,11 +200,11 @@ module Adhearsion
200
200
  # @param opts Hash
201
201
  # * :before specify the plugin to be loaded before another plugin
202
202
  # * :after specify the plugin to be loaded after another plugin
203
- def run(name = nil, opts = {})
203
+ def run(name = nil, opts = {}, &block)
204
204
  name = plugin_name unless name
205
205
  block_given? or raise ArgumentError, "A block must be passed while defining the Plugin run process"
206
206
  opts[:after] ||= runners.last.name unless runners.empty? || runners.find { |i| i.name == opts[:before] }
207
- Adhearsion::Plugin.runners << Initializer.new(name, nil, opts, &Proc.new)
207
+ Adhearsion::Plugin.runners << Initializer.new(name, self, opts, &block)
208
208
  end
209
209
 
210
210
  def count
@@ -74,7 +74,7 @@ module Adhearsion
74
74
 
75
75
  def final_shutdown
76
76
  Adhearsion.active_calls.each do |_, call|
77
- call.hangup!
77
+ call.hangup
78
78
  end
79
79
 
80
80
  # This should shut down any remaining threads. Once those threads have
@@ -7,6 +7,7 @@ module Adhearsion
7
7
  autoload :Initializer
8
8
 
9
9
  config :punchblock do
10
+ enabled true , :transform => Proc.new { |v| v == 'true' }, :desc => "Enable or disable Punchblock connectivity to a Voice server"
10
11
  platform :xmpp , :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
11
12
  Platform punchblock shall use to connect to the Telephony provider. Currently supported values:
12
13
  - :xmpp
@@ -28,11 +29,11 @@ module Adhearsion
28
29
  end
29
30
 
30
31
  init :punchblock do
31
- Initializer.init
32
+ Initializer.init if config.enabled
32
33
  end
33
34
 
34
35
  run :punchblock do
35
- Initializer.run
36
+ Initializer.run if config.enabled
36
37
  end
37
38
 
38
39
  class << self
@@ -109,30 +109,28 @@ module Adhearsion
109
109
  end
110
110
 
111
111
  def connect_to_server
112
- begin
113
- logger.info "Starting connection to server"
114
- client.run
115
- rescue Punchblock::DisconnectedError => e
116
- # We only care about disconnects if the process is up or booting
117
- return unless [:booting, :running].include? Adhearsion::Process.state_name
112
+ logger.info "Starting connection to server"
113
+ client.run
114
+ rescue Punchblock::DisconnectedError => e
115
+ # We only care about disconnects if the process is up or booting
116
+ return unless [:booting, :running].include? Adhearsion::Process.state_name
118
117
 
119
- Adhearsion::Process.reset unless Adhearsion::Process.state_name == :booting
118
+ Adhearsion::Process.reset unless Adhearsion::Process.state_name == :booting
120
119
 
121
- self.attempts += 1
120
+ self.attempts += 1
122
121
 
123
- if self.attempts >= self.config.reconnect_attempts
124
- logger.fatal "Connection lost. Connection retry attempts exceeded."
125
- Adhearsion::Process.stop!
126
- return
127
- end
128
-
129
- logger.error "Connection lost. Attempting reconnect #{self.attempts} of #{self.config.reconnect_attempts}"
130
- sleep self.config.reconnect_timer
131
- retry
132
- rescue Punchblock::ProtocolError => e
133
- logger.fatal "The connection failed due to a protocol error: #{e.name}."
134
- raise e
122
+ if self.attempts >= self.config.reconnect_attempts
123
+ logger.fatal "Connection lost. Connection retry attempts exceeded."
124
+ Adhearsion::Process.stop!
125
+ return
135
126
  end
127
+
128
+ logger.error "Connection lost. Attempting reconnect #{self.attempts} of #{self.config.reconnect_attempts}"
129
+ sleep self.config.reconnect_timer
130
+ retry
131
+ rescue Punchblock::ProtocolError => e
132
+ logger.fatal "The connection failed due to a protocol error: #{e.name}."
133
+ raise e
136
134
  end
137
135
 
138
136
  def dispatch_offer(offer)
@@ -142,7 +140,7 @@ module Adhearsion
142
140
  when :booting, :rejecting
143
141
  logger.info "Declining call because the process is not yet running."
144
142
  call.reject :decline
145
- when :running
143
+ when :running, :stopping
146
144
  Adhearsion.router.handle call
147
145
  else
148
146
  call.reject :error