AsteriskRuby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +174 -0
- data/examples/AGIMenu/config/menu.yaml +78 -0
- data/examples/AGIMenu/example1-filename.rb +64 -0
- data/examples/AGIMenu/example2-file.rb +65 -0
- data/examples/AGIMenu/example3-yaml.rb +65 -0
- data/examples/AGIMenu/example4-hash.rb +74 -0
- data/examples/AGIMenu/example5-empty.rb +65 -0
- data/examples/AGISelection/example1.rb +68 -0
- data/examples/AGIServer/config/example-config.yaml +64 -0
- data/examples/AGIServer/example1-routing.rb +105 -0
- data/examples/AGIServer/example2-agiblock.rb +91 -0
- data/examples/AGIServer/example3-agiparamsblock.rb +92 -0
- data/examples/AGIState/example1.rb +65 -0
- data/lib/AGI.rb +1072 -0
- data/lib/AGIExceptions.rb +139 -0
- data/lib/AGIFramework.rb +69 -0
- data/lib/AGIMenu.rb +244 -0
- data/lib/AGIResponse.rb +160 -0
- data/lib/AGIRoute.rb +72 -0
- data/lib/AGIRouter.rb +153 -0
- data/lib/AGISelection.rb +155 -0
- data/lib/AGIServer.rb +295 -0
- data/lib/AGIState.rb +91 -0
- data/lib/AsteriskRuby.rb +65 -0
- data/tests/AGI/tests.rb +519 -0
- data/tests/AGIMenu/config/menu.yaml +79 -0
- data/tests/AGIMenu/tests.rb +179 -0
- data/tests/AGISelection/tests.rb +104 -0
- data/tests/AGIServer/tests.rb +224 -0
- data/tests/AGIState/tests.rb +80 -0
- metadata +100 -0
data/lib/AGIServer.rb
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Copyright (c) 2007, Vonage Holdings
|
3
|
+
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
* Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
* Redistributions in binary form must reproduce the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
13
|
+
documentation and/or other materials provided with the distribution.
|
14
|
+
* Neither the name of Vonage Holdings nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
22
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
Author: Michael Komitee <mkomitee@gmail.com>
|
31
|
+
|
32
|
+
There are several ways to use the AGIServer module. All of them have a few things in common. In general, since we're creating a server, we need a way to cleanly kill it. So we setup sigint anf sigterm handlers to shutdown all instances of AGIServer Objects
|
33
|
+
|
34
|
+
trap('INT') { AGIServer.shutdown }
|
35
|
+
trap('TERM') { AGIServer.shutdown }
|
36
|
+
|
37
|
+
We also tend to use a logger because this should be daemonized. While developing, I reccomend you log to STDERR
|
38
|
+
|
39
|
+
logger = Logger.new(STDERR)
|
40
|
+
logger.level = Logger::DEBUG
|
41
|
+
|
42
|
+
I use YAML for configuration options. This just sets up the bind port, address, and some threading configuration options.
|
43
|
+
|
44
|
+
config = YAML.load_file('config/example-config.yaml')
|
45
|
+
config[:logger] = logger
|
46
|
+
config[:params] = {:custom1 => 'data1'}
|
47
|
+
|
48
|
+
And then we generate our server
|
49
|
+
|
50
|
+
begin
|
51
|
+
MyAgiServer = AGIServer.new(config)
|
52
|
+
rescue Errno::EADDRINUSE
|
53
|
+
error = "Cannot start MyAgiServer, Address already in use."
|
54
|
+
logger.fatal(error)
|
55
|
+
print "#{error}\n"
|
56
|
+
exit
|
57
|
+
else
|
58
|
+
print "#{$$}"
|
59
|
+
end
|
60
|
+
|
61
|
+
In this example, I'll show you the rails-routing means of working with the AGIServer. Define a Route class along with a few routes, and start the server.
|
62
|
+
|
63
|
+
class TestRoutes < AGIRoute
|
64
|
+
def sample
|
65
|
+
agi.answer
|
66
|
+
print "CUSTOM1 = [#{params[:custom1]}]\n"
|
67
|
+
print "URI = [#{request[:uri]}]\n"
|
68
|
+
print "ID = [#{request[:id]}]\n"
|
69
|
+
print "METHOD = [#{request[:method]}]\n"
|
70
|
+
print "OPTIONS = #{request[:options].pretty_inspect}"
|
71
|
+
print "FOO = [#{request[:options]['foo']}]\n"
|
72
|
+
print '-' * 10 + "\n"
|
73
|
+
helper_method
|
74
|
+
agi.hangup
|
75
|
+
end
|
76
|
+
private
|
77
|
+
def helper_method
|
78
|
+
print "I'm private which means I'm not accessible as a route!\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
MyAgiServer.start
|
82
|
+
MyAgiServer.finish
|
83
|
+
|
84
|
+
Pointing an asterisk extension at agi://localhost:4573/TestRoutes/sample/1/?foo=bar will execute the sample method in the TestRoutes class.
|
85
|
+
|
86
|
+
In this example, I'll show you how to use a block to define the AGI logic. Simply start the server and pass it a block expecting an agi object:
|
87
|
+
|
88
|
+
MyAgiServer.start do |agi|
|
89
|
+
agi.answer
|
90
|
+
puts "I'm Alive!"
|
91
|
+
agi.hangup
|
92
|
+
end
|
93
|
+
MyAgiServer.finish
|
94
|
+
|
95
|
+
In this example, I'll show you another way to use a block to define the AGI logic. This block makes configuration parameters available during the call:
|
96
|
+
|
97
|
+
MyAgiServer.start do |agi,params|
|
98
|
+
agi.answer
|
99
|
+
print "PARAMS = #{params.pretty_inspect}"
|
100
|
+
agi.hangup
|
101
|
+
end
|
102
|
+
MyAgiServer.finish
|
103
|
+
|
104
|
+
=end
|
105
|
+
#AGIServer is a threaded server framework that is intended to be used to communicate with an Asterisk PBX via the Asterisk Gateway Interface, an interface for adding functionality to asterisk. This class implements a server object which will listen on a tcp host:port and accept connections, setup an AGI object, and either yield to a supplied block, which itself defines callflow, or route to public methods of the AGIRoute objects.
|
106
|
+
require 'socket'
|
107
|
+
require 'thread'
|
108
|
+
require 'logger'
|
109
|
+
require 'AGI.rb'
|
110
|
+
require 'AGIExceptions'
|
111
|
+
require 'AGIRouter'
|
112
|
+
|
113
|
+
#AGIServer is a threaded server framework that is intended to be used to communicate with an Asterisk PBX via the Asterisk Gateway Interface, an interface for adding functionality to asterisk. This class implements a server object which will listen on a tcp host:port and accept connections, setup an AGI object, and either yield to a supplied block, which itself defines callflow, or route to public methods of the AGIRoute objects.
|
114
|
+
class AGIServer
|
115
|
+
#A list of all current AGIServers
|
116
|
+
@@servers = []
|
117
|
+
#Binding Parameters supplied during initialization.
|
118
|
+
attr_reader :bind_host, :bind_port
|
119
|
+
#Creates an AGIServer Object based on the provided Parameter Hash, and binds to the appropriate host/port. Will also set signal handlers that will shut down all AGIServer's upon receipt of SIGINT or SIGTERM.
|
120
|
+
#* :bind_host sets the hostname or ip address to bind to. Defaults to localhost.
|
121
|
+
#* :bind_port sets the port to bind to. Defaults to 4573.
|
122
|
+
#* :max_workers sets the maximum number of worker threads to allow for connection processing. Defaults to 10
|
123
|
+
#* :min_workers sets the minimum number of worker threads to maintain for connection processing. Defaults to 5
|
124
|
+
#* :jobs_per_worker sets the number of connections each worker will handle before exiting. Defaults to 50
|
125
|
+
#* :logger sets the Logger object to use for logging. Defaults to Logger.new(STDERR).
|
126
|
+
#* :params can be any object you wish to be made available to all workers; I suggest a hash of objects.
|
127
|
+
def initialize(params={})
|
128
|
+
#Options
|
129
|
+
@bind_host = params[:bind_host] || 'localhost'
|
130
|
+
@bind_port = params[:bind_port] || 4573
|
131
|
+
@max_workers = params[:max_workers] || 10
|
132
|
+
@min_workers = params[:min_workers] || 5
|
133
|
+
@jobs_per_worker = params[:jobs_per_worker] || 50
|
134
|
+
@logger = params[:logger] || Logger.new(STDERR)
|
135
|
+
@stats = params[:stats] || false
|
136
|
+
@params = params[:params] || Hash.new
|
137
|
+
|
138
|
+
#Threads
|
139
|
+
@listener = nil
|
140
|
+
@monitor = nil
|
141
|
+
@workers = []
|
142
|
+
|
143
|
+
#Synchronization
|
144
|
+
@worker_queue = Queue.new
|
145
|
+
@shutdown = false
|
146
|
+
|
147
|
+
#Initial Bind
|
148
|
+
begin
|
149
|
+
@listen_socket = TCPServer.new(@bind_host, @bind_port)
|
150
|
+
rescue Errno::EADDRINUSE
|
151
|
+
@logger.fatal("AGIServer cannot bind to #{@bind_host}:#{@bind_port}, Address already in use.")
|
152
|
+
raise
|
153
|
+
end
|
154
|
+
|
155
|
+
#Track for signal handling
|
156
|
+
@@servers << self
|
157
|
+
AGIRouter.logger(@logger)
|
158
|
+
|
159
|
+
trap('INT') { shutdown }
|
160
|
+
trap('TERM') { shutdown }
|
161
|
+
end
|
162
|
+
#call-seq:
|
163
|
+
# run()
|
164
|
+
# run() { |agi| block }
|
165
|
+
# run() { |agi,params| block }
|
166
|
+
#
|
167
|
+
#Starts the server to run. If a block is provided, the block will be run by all workers to handle connections. If a block is not provided, will attempt to route calls to public methods of AGIRoute objects.
|
168
|
+
#
|
169
|
+
#1. Listener Thread: The Listener Thread is the simplest of the Threads. It accepts client sockets from the main socket, and enqueues those client sockets into the worker_queue.
|
170
|
+
#2. Worker Threads: The Worker Thread is also fairly simple. It loops jobs_per_worker times, and each time, dequeues from the worker_queue. If the result is nil, it exits, otherwise, it interacts with the client socket, either yielding to the aforementioned supplied block or routing to the AGIRoutes. If a Worker Thread is instantiated, it will continue to process requests until it processes jobs_per_worker jobs or the server is stopped.
|
171
|
+
#3. Monitor Thread: The Monitor Thread is the most complex of the threads at use. It instantiates Worker Threads if at any time it detects that there are fewer workers than min_workers, and if at any time it detects that the worker_queue length is greater than zero while there are fewer than max_workers.
|
172
|
+
def run(&block)
|
173
|
+
@logger.info{"AGIServer Initializing Monitor Thread"}
|
174
|
+
@monitor = Thread.new do
|
175
|
+
poll = 0
|
176
|
+
while ! @shutdown do
|
177
|
+
poll += 1
|
178
|
+
if (@workers.length < @max_workers and @worker_queue.length > 0) or ( @workers.length < @min_workers ) then
|
179
|
+
@logger.info{"AGIServer Starting Worker Thread to handle requests"}
|
180
|
+
|
181
|
+
#Begin Worker Thread
|
182
|
+
worker_thread = Thread.new do
|
183
|
+
@jobs_per_worker.times do
|
184
|
+
client = @worker_queue.deq
|
185
|
+
break if client.nil?
|
186
|
+
@logger.debug{"AGIServer Worker received Connection"}
|
187
|
+
agi = AGI.new({ :input => client, :output => client, :logger => @logger })
|
188
|
+
begin
|
189
|
+
agi.init
|
190
|
+
params = @params
|
191
|
+
if block.nil?
|
192
|
+
router = AGIRouter.new(agi.channel_params['request'])
|
193
|
+
router.route(agi, params)
|
194
|
+
else
|
195
|
+
if block.arity == 2
|
196
|
+
yield(agi, params)
|
197
|
+
elsif block.arity == 1
|
198
|
+
yield(agi)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
rescue AGIHangupError => error
|
202
|
+
@logger.error{"AGIServer Worker Caught Unhandled Hangup: #{error}"}
|
203
|
+
rescue AGIError => error
|
204
|
+
@logger.error{"AGIServer Worker Caught Unhandled Exception: #{error.class} #{error.to_s}"}
|
205
|
+
rescue Exception => error
|
206
|
+
@logger.error{"AGIServer Worker Got Unhandled Exception: #{error.class} #{error}"}
|
207
|
+
ensure
|
208
|
+
client.close
|
209
|
+
@logger.debug{"AGIServer Worker done with Connection"}
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@workers.delete(Thread.current)
|
213
|
+
@logger.info{"AGIServer Worker handled last Connection, terminating"}
|
214
|
+
end
|
215
|
+
#End Worker Thread
|
216
|
+
|
217
|
+
@workers << worker_thread
|
218
|
+
next #Short Circuit back without a sleep in case we need more threads for load
|
219
|
+
end
|
220
|
+
if @stats and poll % 10 == 0 then
|
221
|
+
@logger.debug{"AGIServer #{@workers.length} active workers, #{@worker_queue.length} jobs waiting"}
|
222
|
+
end
|
223
|
+
sleep 1
|
224
|
+
end
|
225
|
+
@logger.debug{"AGIServer Signaling all Worker Threads to finish up and exit"}
|
226
|
+
@workers.length.times{ @worker_queue.enq(nil) }
|
227
|
+
@workers.each { |worker| worker.join }
|
228
|
+
@logger.debug{"AGIServer Final Worker Thread closed"}
|
229
|
+
end
|
230
|
+
|
231
|
+
@logger.info{"AGIServer Initializing Listener Thread"}
|
232
|
+
@listener = Thread.new do
|
233
|
+
begin
|
234
|
+
while( client = @listen_socket.accept )
|
235
|
+
@logger.debug{"AGIServer Listener received Connection Request"}
|
236
|
+
@worker_queue.enq(client)
|
237
|
+
end
|
238
|
+
rescue IOError
|
239
|
+
# Occurs on socket shutdown.
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
alias_method :start, :run
|
244
|
+
|
245
|
+
#Will wait for the Monitor and Listener threads to join. The Monitor thread itself will wait for all of it's instantiated Worker threads to join.
|
246
|
+
def join
|
247
|
+
@listener.join && @logger.debug{"AGIServer Listener Thread closed"}
|
248
|
+
@monitor.join && @logger.debug{"AGIServer Monitor Thread closed"}
|
249
|
+
end
|
250
|
+
alias_method :finish, :join
|
251
|
+
|
252
|
+
#Closes the listener socket, so that no new requests will be accepted. Signals to the Monitor thread to shutdown it's Workers when they're done with their current clients.
|
253
|
+
def shutdown
|
254
|
+
@logger.info{"AGIServer Shutting down gracefully"}
|
255
|
+
@listen_socket.close && @logger.info{"AGIServer No longer accepting connections"}
|
256
|
+
@shutdown = true && @logger.info{"AGIServer Signaling Monitor to close after active sessions complete"}
|
257
|
+
end
|
258
|
+
alias_method :stop, :shutdown
|
259
|
+
|
260
|
+
#Calls shutdown on all AGIServer objects.
|
261
|
+
def AGIServer.shutdown
|
262
|
+
@@servers.each { |server| server.shutdown }
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
=begin
|
268
|
+
Copyright (c) 2007, Vonage Holdings
|
269
|
+
|
270
|
+
All rights reserved.
|
271
|
+
|
272
|
+
Redistribution and use in source and binary forms, with or without
|
273
|
+
modification, are permitted provided that the following conditions are met:
|
274
|
+
|
275
|
+
* Redistributions of source code must retain the above copyright
|
276
|
+
notice, this list of conditions and the following disclaimer.
|
277
|
+
* Redistributions in binary form must reproduce the above copyright
|
278
|
+
notice, this list of conditions and the following disclaimer in the
|
279
|
+
documentation and/or other materials provided with the distribution.
|
280
|
+
* Neither the name of Vonage Holdings nor the names of its
|
281
|
+
contributors may be used to endorse or promote products derived from this
|
282
|
+
software without specific prior written permission.
|
283
|
+
|
284
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
285
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
286
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
287
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
288
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
289
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
290
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
291
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
292
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
293
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
294
|
+
POSSIBILITY OF SUCH DAMAGE.
|
295
|
+
=end
|
data/lib/AGIState.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Copyright (c) 2007, Vonage Holdings
|
3
|
+
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
* Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
* Redistributions in binary form must reproduce the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
13
|
+
documentation and/or other materials provided with the distribution.
|
14
|
+
* Neither the name of Vonage Holdings nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
22
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
Author: Michael Komitee <mkomitee@gmail.com>
|
31
|
+
|
32
|
+
AGIState is meant to be subclassed to implement any state that needs to persist throughout an AGI session within the framework. By default, it can be sed to increase and reset failiure counters, and have an AGIStateException thrown when conditions are met.
|
33
|
+
=end
|
34
|
+
|
35
|
+
require 'AGIExceptions'
|
36
|
+
#AGIState is meant to be subclassed to implement any state that needs to persist throughout an AGI session within the framework. By default, it can be sed to increase and reset failiure counters, and have an AGIStateException thrown when conditions are met.
|
37
|
+
class AGIState
|
38
|
+
@@failure_threshold = 3
|
39
|
+
attr_reader :failures
|
40
|
+
attr_reader :failure_threshold
|
41
|
+
def initialize(conf={})
|
42
|
+
@failures = 0
|
43
|
+
@failure_threshold = conf[:threshold] || @@failure_threshold
|
44
|
+
@failure_threshold = conf[:failure_threshold] || @failure_threshold
|
45
|
+
end
|
46
|
+
def self.failure_threshold=(threshold)
|
47
|
+
@@failure_threshold = threshold
|
48
|
+
end
|
49
|
+
def failure_inc
|
50
|
+
@failures += 1
|
51
|
+
if @failures >= @failure_threshold then
|
52
|
+
raise AGIStateFailure.new("Too many failures ( #{@failures} >= #{@failure_threshold})" )
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def failure_reset
|
56
|
+
@failures = 0
|
57
|
+
if @failures >= @failure_threshold then
|
58
|
+
raise AGIStateFailure.new("Too many failures ( #{@failures} >= #{@failure_threshold})")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
=begin
|
64
|
+
Copyright (c) 2007, Vonage Holdings
|
65
|
+
|
66
|
+
All rights reserved.
|
67
|
+
|
68
|
+
Redistribution and use in source and binary forms, with or without
|
69
|
+
modification, are permitted provided that the following conditions are met:
|
70
|
+
|
71
|
+
* Redistributions of source code must retain the above copyright
|
72
|
+
notice, this list of conditions and the following disclaimer.
|
73
|
+
* Redistributions in binary form must reproduce the above copyright
|
74
|
+
notice, this list of conditions and the following disclaimer in the
|
75
|
+
documentation and/or other materials provided with the distribution.
|
76
|
+
* Neither the name of Vonage Holdings nor the names of its
|
77
|
+
contributors may be used to endorse or promote products derived from this
|
78
|
+
software without specific prior written permission.
|
79
|
+
|
80
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
81
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
82
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
83
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
84
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
85
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
86
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
87
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
88
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
89
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
90
|
+
POSSIBILITY OF SUCH DAMAGE.
|
91
|
+
=end
|
data/lib/AsteriskRuby.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Copyright (c) 2007, Vonage Holdings
|
3
|
+
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
* Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
* Redistributions in binary form must reproduce the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
13
|
+
documentation and/or other materials provided with the distribution.
|
14
|
+
* Neither the name of Vonage Holdings nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from this
|
16
|
+
software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
22
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
23
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
24
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
25
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
26
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
27
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
28
|
+
POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
30
|
+
Author: Michael Komitee <mkomitee@gmail.com>
|
31
|
+
|
32
|
+
This is a meta package for the framework at large.
|
33
|
+
=end
|
34
|
+
|
35
|
+
require 'AGIFramework'
|
36
|
+
|
37
|
+
=begin
|
38
|
+
Copyright (c) 2007, Vonage Holdings
|
39
|
+
|
40
|
+
All rights reserved.
|
41
|
+
|
42
|
+
Redistribution and use in source and binary forms, with or without
|
43
|
+
modification, are permitted provided that the following conditions are met:
|
44
|
+
|
45
|
+
* Redistributions of source code must retain the above copyright
|
46
|
+
notice, this list of conditions and the following disclaimer.
|
47
|
+
* Redistributions in binary form must reproduce the above copyright
|
48
|
+
notice, this list of conditions and the following disclaimer in the
|
49
|
+
documentation and/or other materials provided with the distribution.
|
50
|
+
* Neither the name of Vonage Holdings nor the names of its
|
51
|
+
contributors may be used to endorse or promote products derived from this
|
52
|
+
software without specific prior written permission.
|
53
|
+
|
54
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
55
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
56
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
57
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
58
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
59
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
60
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
61
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
62
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
63
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
64
|
+
POSSIBILITY OF SUCH DAMAGE.
|
65
|
+
=end
|