adhearsion 2.0.0.rc5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,24 +1,21 @@
1
- Adhearsion
2
- ===========
1
+ # Adhearsion
3
2
 
4
3
  Adhearsion is an open-source voice application development framework. Adhearsion users write applications atop the framework with Ruby and **call into their code**.
5
4
 
6
5
  Adhearsion rests above a lower-level telephony platform, for example [Asterisk](http://asterisk.org) or [Voxeo PRISM](http://voxeolabs.com/prism/), and provides a framework for integrating with various resources, such as SQL, LDAP and XMPP (Jabber).
7
6
 
8
- Features
9
- --------
7
+ ## Features
10
8
 
11
- * An elegant system of call controllers for writing the code which controls a live phone call.
12
- * An events subsystem which maintains a Thread-pool for executing your namespaced callbacks.
13
- * A very useful plugin architecture with which you may write Adhearsion plugins and share them with the world via RubyGems.
14
- * JRuby compatibility for running atop the Java Virtual Machine and using virtually any Java library.
15
- * Ability to re-use existing Ruby on Rails database models with ActiveRecord/ActiveLDAP
16
- * Easy interactive communication via XMPP instant messages using the Blather library
17
- * Strong test coverage
18
- * Much more
9
+ * Simple Ruby code
10
+ * Flexible CallControllers to handle calls
11
+ * High-level media handling constructs
12
+ * Simple interaction between calls
13
+ * Self-documenting configuration engine
14
+ * Support for plugins and other code reuse
15
+ * Integration with databases, web APIs, etc
16
+ * Event monitoring, async communication
19
17
 
20
- Requirements
21
- ------------
18
+ ## Requirements
22
19
 
23
20
  * Ruby 1.9.2+ or JRuby 1.6.7+
24
21
  * A VoIP platform:
@@ -26,13 +23,11 @@ Requirements
26
23
  * Prism 11+ with rayo-server
27
24
  * An interest in building cool new things
28
25
 
29
- Install
30
- -------
26
+ ## Install
31
27
 
32
28
  `gem install adhearsion`
33
29
 
34
- Examples
35
- --------
30
+ ## Examples
36
31
 
37
32
  An Adhearsion application can be as simple as this:
38
33
 
@@ -45,17 +40,13 @@ hangup
45
40
 
46
41
  For more examples, check out [the website](http://adhearsion.com/examples).
47
42
 
48
- Documentation
49
- =============
43
+ ## Documentation
50
44
 
51
45
  Visit [Adhearsion's website](http://adhearsion.com) for code examples and more information about the project. Also checkout the [Adhearsion wiki on Github](http://github.com/adhearsion/adhearsion/wiki) for community documentation.
52
46
 
53
47
  If you're having trouble, you may want to try asking your question on the IRC channel (#adhearsion on irc.freenode.net), [mailing list](http://groups.google.com/group/adhearsion) or, if you've found a bug, report it on the [bug tracker](https://github.com/adhearsion/adhearsion/issues).
54
48
 
55
- Author
56
- ------
57
-
58
- Original author: [Jay Phillips](https://github.com/jicksta)
49
+ ## Author
59
50
 
60
51
  Core team:
61
52
 
@@ -65,13 +56,13 @@ Core team:
65
56
 
66
57
  Contributors: https://github.com/adhearsion/adhearsion/contributors
67
58
 
68
- Contributions
69
- -----------------------------
59
+ Original author: [Jay Phillips](https://github.com/jicksta)
60
+
61
+ ### Contributions
70
62
 
71
63
  Adhearsion has a set of [contribution guidelines](https://github.com/adhearsion/adhearsion/wiki/Contributing) which help to smooth the contribution process.
72
64
  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.
73
65
 
74
- Copyright
75
- ---------
66
+ ### Copyright
76
67
 
77
- Copyright (c) 2011 Individual contributors. GNU LESSER GENERAL PUBLIC LICENSE (see LICENSE for details).
68
+ Copyright (c) 2012 Adhearsion Foundation Inc. MIT LICENSE (see LICENSE for details).
@@ -7,53 +7,44 @@ Gem::Specification.new do |s|
7
7
  s.version = Adhearsion::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Jay Phillips", "Jason Goecke", "Ben Klang", "Ben Langfeld"]
10
- s.email = "dev&Adhearsion.com"
10
+ s.email = "dev@adhearsion.com"
11
11
  s.homepage = "http://adhearsion.com"
12
12
  s.summary = "Adhearsion, open-source telephony development framework"
13
13
  s.description = "Adhearsion is an open-source telephony development framework"
14
- s.date = Date.today.to_s
15
14
 
16
15
  s.files = `git ls-files`.split("\n")
17
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
18
  s.require_paths = ["lib"]
20
19
 
21
- # Runtime dependencies
22
- s.add_runtime_dependency 'bundler', [">= 1.0.10"]
23
- s.add_runtime_dependency 'punchblock', [">= 0.12.0"]
24
- s.add_runtime_dependency 'logging', [">= 1.6.1"]
25
- s.add_runtime_dependency 'adhearsion-loquacious', [">= 1.9.0"]
26
20
  s.add_runtime_dependency 'activesupport', [">= 3.0.10"]
27
- # i18n is only strictly a dependency for ActiveSupport >= 3.0.0
28
- # Since it doesn't conflict with <3.0.0 we'll require it to be
29
- # on the safe side.
30
- s.add_runtime_dependency 'i18n', [">= 0.5.0"]
31
- s.add_runtime_dependency 'json'
32
- s.add_runtime_dependency 'thor'
33
- s.add_runtime_dependency 'rake'
34
- s.add_runtime_dependency 'pry'
35
- s.add_runtime_dependency 'uuid'
36
- s.add_runtime_dependency 'future-resource', [">= 0.0.2"]
37
- s.add_runtime_dependency 'ruby_speech', [">= 0.4.0"]
21
+ s.add_runtime_dependency 'adhearsion-loquacious', [">= 1.9.0"]
22
+ s.add_runtime_dependency 'bundler', ["~> 1.0"]
23
+ s.add_runtime_dependency 'celluloid', [">= 0.10.0"]
38
24
  s.add_runtime_dependency 'countdownlatch'
39
- s.add_runtime_dependency 'has-guarded-handlers', [">= 1.1.0"]
25
+ s.add_runtime_dependency 'deep_merge'
26
+ s.add_runtime_dependency 'ffi', [">= 1.0.11"]
27
+ s.add_runtime_dependency 'future-resource', ["~> 1.0"]
40
28
  s.add_runtime_dependency 'girl_friday'
29
+ s.add_runtime_dependency 'has-guarded-handlers', ["~> 1.1"]
41
30
  s.add_runtime_dependency 'jruby-openssl' if RUBY_PLATFORM == 'java'
42
- s.add_runtime_dependency 'ffi', [">= 1.0.11"]
43
- s.add_runtime_dependency 'celluloid', [">= 0.10.0"]
44
- s.add_runtime_dependency 'deep_merge'
31
+ s.add_runtime_dependency 'logging', [">= 1.6.1"]
32
+ s.add_runtime_dependency 'pry'
33
+ s.add_runtime_dependency 'punchblock', ["~> 1.0"]
34
+ s.add_runtime_dependency 'rake'
35
+ s.add_runtime_dependency 'ruby_speech', ["~> 1.0"]
36
+ s.add_runtime_dependency 'thor'
37
+ s.add_runtime_dependency 'uuid'
45
38
 
46
- # Development dependencies
47
- s.add_development_dependency 'rspec', ["~> 2.7.0"]
39
+ s.add_development_dependency 'aruba'
40
+ s.add_development_dependency 'ci_reporter'
41
+ s.add_development_dependency 'cucumber'
48
42
  s.add_development_dependency 'flexmock'
49
- s.add_development_dependency 'activerecord', [">= 3.0.10"]
43
+ s.add_development_dependency 'guard-cucumber'
44
+ s.add_development_dependency 'guard-rspec'
45
+ s.add_development_dependency 'rspec', ["~> 2.7.0"]
46
+ s.add_development_dependency 'ruby_gntp'
50
47
  s.add_development_dependency 'simplecov'
51
48
  s.add_development_dependency 'simplecov-rcov'
52
- s.add_development_dependency 'ci_reporter'
53
49
  s.add_development_dependency 'yard'
54
- s.add_development_dependency 'guard-rspec'
55
- s.add_development_dependency 'guard-cucumber'
56
- s.add_development_dependency 'ruby_gntp'
57
- s.add_development_dependency 'cucumber'
58
- s.add_development_dependency 'aruba'
59
50
  end
@@ -94,7 +94,10 @@ end
94
94
  Celluloid.exception_handler { |e| Adhearsion::Events.trigger :exception, e }
95
95
 
96
96
  module Celluloid
97
- def self.logger
98
- ::Logging.logger['Celluloid']
97
+ class << self
98
+ undef :logger
99
+ def logger
100
+ ::Logging.logger['Celluloid']
101
+ end
99
102
  end
100
103
  end
@@ -25,7 +25,10 @@ module Adhearsion
25
25
  end
26
26
  end
27
27
 
28
- attr_accessor :offer, :client, :end_reason, :commands, :variables, :controllers
28
+ # @private
29
+ attr_accessor :offer, :client, :end_reason, :commands, :controllers
30
+
31
+ attr_accessor :variables
29
32
 
30
33
  delegate :[], :[]=, :to => :variables
31
34
  delegate :to, :from, :to => :offer, :allow_nil => true
@@ -42,28 +45,46 @@ module Adhearsion
42
45
  self << offer if offer
43
46
  end
44
47
 
48
+ #
49
+ # @return [String, nil] The globally unique ID for the call
50
+ #
45
51
  def id
46
52
  offer.target_call_id if offer
47
53
  end
48
54
 
55
+ #
56
+ # @return [Array] The set of labels with which this call has been tagged.
57
+ #
49
58
  def tags
50
59
  @tags.clone
51
60
  end
52
61
 
53
- # This may still be a symbol, but no longer requires the tag to be a symbol although beware
54
- # that using a symbol would create a memory leak if used improperly
62
+ #
63
+ # Tag a call with an arbitrary label
64
+ #
55
65
  # @param [String, Symbol] label String or Symbol with which to tag this call
66
+ #
56
67
  def tag(label)
57
68
  abort ArgumentError.new "Tag must be a String or Symbol" unless [String, Symbol].include?(label.class)
58
69
  @tags << label
59
70
  end
60
71
 
61
- def remove_tag(symbol)
62
- @tags.reject! { |tag| tag == symbol }
72
+ #
73
+ # Remove a label
74
+ #
75
+ # @param [String, Symbol] label
76
+ #
77
+ def remove_tag(label)
78
+ @tags.reject! { |tag| tag == label }
63
79
  end
64
80
 
65
- def tagged_with?(symbol)
66
- @tags.include? symbol
81
+ #
82
+ # Establish if the call is tagged with the provided label
83
+ #
84
+ # @param [String, Symbol] label
85
+ #
86
+ def tagged_with?(label)
87
+ @tags.include? label
67
88
  end
68
89
 
69
90
  def register_event_handler(*guards, &block)
@@ -74,10 +95,10 @@ module Adhearsion
74
95
  logger.debug "Receiving message: #{message.inspect}"
75
96
  catching_standard_errors { trigger_handler :event, message }
76
97
  end
77
-
78
98
  alias << deliver_message
79
99
 
80
- def register_initial_handlers # :nodoc:
100
+ # @private
101
+ def register_initial_handlers
81
102
  register_event_handler Punchblock::Event::Offer do |offer|
82
103
  @offer = offer
83
104
  @client = offer.client
@@ -110,7 +131,8 @@ module Adhearsion
110
131
  end
111
132
  end
112
133
 
113
- def after_end_hold_time # :nodoc:
134
+ # @private
135
+ def after_end_hold_time
114
136
  30
115
137
  end
116
138
 
@@ -121,6 +143,9 @@ module Adhearsion
121
143
  end
122
144
  end
123
145
 
146
+ #
147
+ # @return [Boolean] if the call is currently active or not (disconnected)
148
+ #
124
149
  def active?
125
150
  !end_reason
126
151
  end
@@ -144,7 +169,8 @@ module Adhearsion
144
169
  write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
145
170
  end
146
171
 
147
- def clear_from_active_calls # :nodoc:
172
+ # @private
173
+ def clear_from_active_calls
148
174
  Adhearsion.active_calls.remove_inactive_call current_actor
149
175
  end
150
176
 
@@ -157,7 +183,7 @@ module Adhearsion
157
183
  # @param [Hash, Optional] options further options to be joined with
158
184
  #
159
185
  def join(target, options = {})
160
- command = Punchblock::Command::Join.new join_options_with_target(target, options)
186
+ command = Punchblock::Command::Join.new options.merge(join_options_with_target(target))
161
187
  write_and_await_response command
162
188
  end
163
189
 
@@ -173,8 +199,9 @@ module Adhearsion
173
199
  write_and_await_response command
174
200
  end
175
201
 
176
- def join_options_with_target(target, options = {})
177
- options.merge(case target
202
+ # @private
203
+ def join_options_with_target(target)
204
+ case target
178
205
  when Call
179
206
  { :call_id => target.id }
180
207
  when String
@@ -184,7 +211,7 @@ module Adhearsion
184
211
  target
185
212
  else
186
213
  abort ArgumentError.new "Don't know how to join to #{target.inspect}"
187
- end)
214
+ end
188
215
  end
189
216
 
190
217
  def wait_for_joined(expected_target)
@@ -209,6 +236,7 @@ module Adhearsion
209
236
  write_and_await_response ::Punchblock::Command::Unmute.new
210
237
  end
211
238
 
239
+ # @private
212
240
  def write_and_await_response(command, timeout = 60)
213
241
  commands << command
214
242
  write_command command
@@ -229,6 +257,7 @@ module Adhearsion
229
257
  abort CommandTimeout.new(command.to_s)
230
258
  end
231
259
 
260
+ # @private
232
261
  def write_command(command)
233
262
  abort Hangup.new(@end_reason) unless active? || command.is_a?(Punchblock::Command::Hangup)
234
263
  variables.merge! command.headers_hash if command.respond_to? :headers_hash
@@ -236,18 +265,22 @@ module Adhearsion
236
265
  client.execute_command command, :call_id => id, :async => true
237
266
  end
238
267
 
239
- def logger_id # :nodoc:
268
+ # @private
269
+ def logger_id
240
270
  "#{self.class}: #{id}"
241
271
  end
242
272
 
243
- def logger # :nodoc:
273
+ # @private
274
+ def logger
244
275
  super
245
276
  end
246
277
 
278
+ # @private
247
279
  def to_ary
248
280
  [current_actor]
249
281
  end
250
282
 
283
+ # @private
251
284
  def inspect
252
285
  attrs = [:offer, :end_reason, :commands, :variables, :controllers, :to, :from].map do |attr|
253
286
  "#{attr}=#{send(attr).inspect}"
@@ -268,24 +301,28 @@ module Adhearsion
268
301
  end.tap { |t| Adhearsion::Process.important_threads << t }
269
302
  end
270
303
 
304
+ # @private
271
305
  def register_controller(controller)
272
306
  @controllers << controller
273
307
  end
274
308
 
309
+ # @private
275
310
  def pause_controllers
276
311
  controllers.each(&:pause!)
277
312
  end
278
313
 
314
+ # @private
279
315
  def resume_controllers
280
316
  controllers.each(&:resume!)
281
317
  end
282
318
 
283
- class CommandRegistry < ThreadSafeArray # :nodoc:
319
+ # @private
320
+ class CommandRegistry < ThreadSafeArray
284
321
  def terminate
285
322
  hangup = Hangup.new
286
323
  each { |command| command.response = hangup if command.requested? }
287
324
  end
288
325
  end
289
326
 
290
- end#Call
291
- end#Adhearsion
327
+ end
328
+ end
@@ -35,6 +35,11 @@ module Adhearsion
35
35
  end
36
36
 
37
37
  class << self
38
+ #
39
+ # Execute a call controller, allowing passing control to another controller
40
+ #
41
+ # @param [CallController] controller
42
+ #
38
43
  def exec(controller)
39
44
  new_controller = catch :pass_controller do
40
45
  controller.execute!
@@ -44,23 +49,35 @@ module Adhearsion
44
49
  exec new_controller if new_controller
45
50
  end
46
51
 
47
- ##
52
+ #
48
53
  # Include another module into all CallController classes
54
+ #
49
55
  def mixin(mod)
50
56
  include mod
51
57
  end
52
58
  end
53
59
 
54
- attr_reader :call, :metadata, :block
60
+ attr_reader :call, :metadata
61
+
62
+ # @private
63
+ attr_reader :block
55
64
 
56
65
  delegate :[], :[]=, :to => :@metadata
57
66
  delegate :variables, :logger, :to => :call
58
67
 
68
+ #
69
+ # Create a new instance
70
+ #
71
+ # @param [Call] call the call to operate the controller on
72
+ # @param [Hash] metadata generic key-value storage applicable to the controller
73
+ # @param block to execute on the call
74
+ #
59
75
  def initialize(call, metadata = nil, &block)
60
76
  @call, @metadata, @block = call, metadata || {}, block
61
77
  end
62
78
 
63
- def execute!(*options) # :nodoc:
79
+ # @private
80
+ def execute!(*options)
64
81
  call.register_controller! self
65
82
  execute_callbacks :before_call
66
83
  run
@@ -73,20 +90,36 @@ module Adhearsion
73
90
  logger.debug "Finished executing controller #{self.inspect}"
74
91
  end
75
92
 
93
+ #
94
+ # Invoke the block supplied when creating the controller
95
+ #
76
96
  def run
77
97
  instance_exec(&block) if block
78
98
  end
79
99
 
100
+ #
101
+ # Invoke another controller class within this controller, returning to this context on completion.
102
+ #
103
+ # @param [Class] controller_class The class of controller to execute
104
+ # @param [Hash] metadata generic key-value storage applicable to the controller
105
+ #
80
106
  def invoke(controller_class, metadata = nil)
81
107
  controller = controller_class.new call, metadata
82
108
  controller.run
83
109
  end
84
110
 
111
+ #
112
+ # Cease execution of this controller, and pass to another.
113
+ #
114
+ # @param [Class] controller_class The class of controller to pass to
115
+ # @param [Hash] metadata generic key-value storage applicable to the controller
116
+ #
85
117
  def pass(controller_class, metadata = nil)
86
118
  throw :pass_controller, controller_class.new(call, metadata)
87
119
  end
88
120
 
89
- def execute_callbacks(type) # :nodoc:
121
+ # @private
122
+ def execute_callbacks(type)
90
123
  self.class.callbacks[type].each do |callback|
91
124
  catching_standard_errors do
92
125
  instance_exec(&callback)
@@ -94,21 +127,29 @@ module Adhearsion
94
127
  end
95
128
  end
96
129
 
97
- def after_call # :nodoc:
130
+ # @private
131
+ def after_call
98
132
  @after_call ||= execute_callbacks :after_call
99
133
  end
100
134
 
135
+ #
136
+ # Hangup the call, and execute after_call callbacks
137
+ #
138
+ # @param [Hash] headers
139
+ #
101
140
  def hangup(headers = nil)
102
141
  block_until_resumed
103
142
  hangup_response = call.hangup headers
104
143
  after_call unless hangup_response == false
105
144
  end
106
145
 
146
+ # @private
107
147
  def write_and_await_response(command)
108
148
  block_until_resumed
109
149
  call.write_and_await_response command
110
150
  end
111
151
 
152
+ # @private
112
153
  def execute_component_and_await_completion(component)
113
154
  write_and_await_response component
114
155
 
@@ -119,33 +160,58 @@ module Adhearsion
119
160
  component
120
161
  end
121
162
 
163
+ #
164
+ # Answer the call
165
+ #
166
+ # @see Call#answer
167
+ #
122
168
  def answer(*args)
123
169
  block_until_resumed
124
170
  call.answer(*args)
125
171
  end
126
172
 
173
+ #
174
+ # Reject the call
175
+ #
176
+ # @see Call#reject
177
+ #
127
178
  def reject(*args)
128
179
  block_until_resumed
129
180
  call.reject(*args)
130
181
  end
131
182
 
183
+ #
184
+ # Mute the call
185
+ #
186
+ # @see Call#mute
187
+ #
132
188
  def mute(*args)
133
189
  block_until_resumed
134
190
  call.mute(*args)
135
191
  end
136
192
 
193
+ #
194
+ # Unmute the call
195
+ #
196
+ # @see Call#unmute
197
+ #
137
198
  def unmute(*args)
138
199
  block_until_resumed
139
200
  call.unmute(*args)
140
201
  end
141
202
 
203
+ #
204
+ # Join the call to another call or a mixer, and block until the call is unjoined (by hangup or otherwise).
205
+ #
206
+ # @param [Object] target See Call#join for details
207
+ # @param [Hash] options
208
+ # @option options [Boolean] :async Return immediately, without waiting for the calls to unjoin. Defaults to false.
209
+ #
210
+ # @see Call#join
211
+ #
142
212
  def join(target, options = {})
143
- async = if target.is_a?(Hash)
144
- target.delete :async
145
- else
146
- options.delete :async
147
- end
148
213
  block_until_resumed
214
+ async = (target.is_a?(Hash) ? target : options).delete :async
149
215
  join_command = call.join target, options
150
216
  waiter = join_command.call_id || join_command.mixer_name
151
217
  if async
@@ -155,20 +221,24 @@ module Adhearsion
155
221
  end
156
222
  end
157
223
 
158
- def block_until_resumed # :nodoc:
224
+ # @private
225
+ def block_until_resumed
159
226
  instance_variable_defined?(:@pause_latch) && @pause_latch.wait
160
227
  end
161
228
 
162
- def pause! # :nodoc:
229
+ # @private
230
+ def pause!
163
231
  @pause_latch = CountDownLatch.new 1
164
232
  end
165
233
 
166
- def resume! # :nodoc:
234
+ # @private
235
+ def resume!
167
236
  return unless @pause_latch
168
237
  @pause_latch.countdown!
169
238
  @pause_latch = nil
170
239
  end
171
240
 
241
+ # @private
172
242
  def inspect
173
243
  "#<#{self.class} call=#{call.id}, metadata=#{metadata.inspect}>"
174
244
  end