adhearsion 2.2.1 → 2.3.0

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.
@@ -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