adhearsion 2.0.0.rc5 → 2.0.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.
@@ -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