adhearsion 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +339 -0
- data/Rakefile +108 -0
- data/ahn +195 -0
- data/lib/adhearsion.rb +402 -0
- data/lib/constants.rb +20 -0
- data/lib/core_extensions.rb +157 -0
- data/lib/database_functions.rb +76 -0
- data/lib/rami.rb +822 -0
- data/lib/servlet_container.rb +146 -0
- data/new_projects/Rakefile +100 -0
- data/new_projects/config/adhearsion.sqlite3 +0 -0
- data/new_projects/config/adhearsion.yml +11 -0
- data/new_projects/config/database.rb +50 -0
- data/new_projects/config/database.yml +10 -0
- data/new_projects/config/helpers/drb_server.yml +43 -0
- data/new_projects/config/helpers/factorial.alien.c.yml +1 -0
- data/new_projects/config/helpers/manager_proxy.yml +7 -0
- data/new_projects/config/helpers/micromenus.yml +1 -0
- data/new_projects/config/helpers/micromenus/collab.rb +55 -0
- data/new_projects/config/helpers/micromenus/images/tux.bmp +0 -0
- data/new_projects/config/helpers/micromenus/javascripts/builder.js +131 -0
- data/new_projects/config/helpers/micromenus/javascripts/controls.js +834 -0
- data/new_projects/config/helpers/micromenus/javascripts/dragdrop.js +944 -0
- data/new_projects/config/helpers/micromenus/javascripts/effects.js +956 -0
- data/new_projects/config/helpers/micromenus/javascripts/prototype.js +2319 -0
- data/new_projects/config/helpers/micromenus/javascripts/scriptaculous.js +51 -0
- data/new_projects/config/helpers/micromenus/javascripts/slider.js +278 -0
- data/new_projects/config/helpers/micromenus/javascripts/unittest.js +557 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.css +10 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.xul.css +44 -0
- data/new_projects/config/helpers/weather.yml +1 -0
- data/new_projects/config/helpers/xbmc.yml +1 -0
- data/new_projects/config/migration.rb +53 -0
- data/new_projects/extensions.rb +56 -0
- data/new_projects/helpers/drb_server.rb +32 -0
- data/new_projects/helpers/factorial.alien.c +32 -0
- data/new_projects/helpers/manager_proxy.rb +43 -0
- data/new_projects/helpers/micromenus.rb +374 -0
- data/new_projects/helpers/oscar_wilde_quotes.rb +197 -0
- data/new_projects/helpers/weather.rb +85 -0
- data/new_projects/helpers/xbmc.rb +12 -0
- data/new_projects/logs/database.log +0 -0
- data/test/core_extensions_test.rb +26 -0
- data/test/dial_test.rb +43 -0
- data/test/stress_tests/test.rb +13 -0
- data/test/stress_tests/test.yml +13 -0
- data/test/test_micromenus.rb +0 -0
- metadata +131 -0
data/lib/adhearsion.rb
ADDED
@@ -0,0 +1,402 @@
|
|
1
|
+
# Adhearsion, open source technology integrator
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'core_extensions'
|
19
|
+
|
20
|
+
module Asterisk
|
21
|
+
|
22
|
+
# The exec method allows any traditional Asterisk applications to be called
|
23
|
+
# within Adhearsion. The first argument should be the case-insensitive name
|
24
|
+
# of the application and any arguments needed by the application can simply
|
25
|
+
# by trailed onto the end of the method. For a full list of Asterisk's
|
26
|
+
# applications, see "this":http://www.voip-info.org/wiki/index.php?page=Asterisk+-+documentation+of+application+commands
|
27
|
+
def exec(app, *options)
|
28
|
+
result = rawr "EXEC #{app} " + (options * '|')
|
29
|
+
result = result[/-?\d+$/]
|
30
|
+
result == "-2" ? false : result
|
31
|
+
end
|
32
|
+
|
33
|
+
# This method of receiving user input uses the fantastic Asterisk
|
34
|
+
# Read() application, but its results are pretty inconsitent over
|
35
|
+
# AGI. This code has been kept here in case these bugs are fixed.
|
36
|
+
def old_input digits=nil, user_options=Hash.new('')
|
37
|
+
used_options = {:variable => String.random, :digits => digits}
|
38
|
+
used_options.default = ''
|
39
|
+
used_options.merge! user_options
|
40
|
+
args = []
|
41
|
+
%w(variable soundfile digits option attempts timeout).each do |x|
|
42
|
+
args << used_options[x.to_sym]
|
43
|
+
end
|
44
|
+
log "THESE ARE THE INPUT ARGS: " + args.inspect
|
45
|
+
exec :read, args
|
46
|
+
$OSCAR[:VARS] << args.first
|
47
|
+
x = get_variable(args.first).gsub(/[\(\)]/, "")
|
48
|
+
puts "RETURN: #{x.inspect}"
|
49
|
+
x.simplify
|
50
|
+
end
|
51
|
+
|
52
|
+
# Input is used to receive keypad input from the user, pausing until they
|
53
|
+
# have entered the desired number of digits (specified with the first
|
54
|
+
# parameter) or the timeout has been reached (specified as a hash argument
|
55
|
+
# with the key :timeout). By default, there is no timeout, waiting infinitely.
|
56
|
+
#
|
57
|
+
# If you desire a sound to be played other than a simple beep to instruct
|
58
|
+
# the callee to input data, pass the filename as an hash argument with either
|
59
|
+
# the :play or :file key.
|
60
|
+
#
|
61
|
+
# When called without any arguments (or a first argument of -1), the user is
|
62
|
+
# able to enter digits ad infinitum until they press the pound (#) key.
|
63
|
+
def input digits=nil, hash={}
|
64
|
+
timeout, file = (hash[:timeout] || -1), (hash[:play] || hash[:file] || 'beep')
|
65
|
+
result = rawr "GET DATA #{file} #{timeout} #{digits}"
|
66
|
+
result = result[/\=-?\d+/]
|
67
|
+
result ? result[1..-1] : false
|
68
|
+
end
|
69
|
+
|
70
|
+
# Does as you'd imagine: returns the amount of time the given block takes to
|
71
|
+
# execute. This is particularly useful in VoIP since billing and such often
|
72
|
+
# requires time keeping beyond the Call Detail Records. This method is also
|
73
|
+
# aliased to bill() as well.
|
74
|
+
def time
|
75
|
+
return 0 unless block_given?
|
76
|
+
start = Time.now
|
77
|
+
yield start
|
78
|
+
Time.now - start
|
79
|
+
end
|
80
|
+
alias bill time
|
81
|
+
|
82
|
+
# Requires an AMI connection! Given a block, everything contained
|
83
|
+
# within it will be recorded. If no arguments are given, a String
|
84
|
+
# for the filename will be generated from the current time and a
|
85
|
+
# sequence of random alphanumeric characters.
|
86
|
+
def record hash, &block
|
87
|
+
defaults = {:file => "#{String.random(5)}", :folder => nil,
|
88
|
+
:channel => Thread.current[:VARS]['channel'], :format => 'wav', :mix => '1'}
|
89
|
+
defaults = defaults.merge hash
|
90
|
+
#PBX.record
|
91
|
+
end
|
92
|
+
|
93
|
+
# Waits for single digit numpad key response from the user. If no timeout argument
|
94
|
+
# is given, the system will wait indefinitely. The argument is the desired time
|
95
|
+
# to wait fo0r the response in seconds. Feel free to use the ActiveSupport extensions
|
96
|
+
# for using this method like wait_for_digit(2.minutes) or something similar. When the
|
97
|
+
# timeout is encountered (or an error occurs receiving the input), the method
|
98
|
+
# returns nil. If the asterisk or pound key was pressed, a String is returned of that
|
99
|
+
# response. In all other cases, the Fixnum of the number pressed is returned.
|
100
|
+
def wait_for_digit timeout=-1
|
101
|
+
digit = rawr("WAIT FOR DIGIT #{timeout < 0 ? -1 : timeout / 1000.0}").match(/=(.*)$/)[1].to_i
|
102
|
+
return nil if digit <= 0 # If there was an error or timeout
|
103
|
+
return digit.chr if [32,45].include? digit
|
104
|
+
digit - ?0
|
105
|
+
end
|
106
|
+
|
107
|
+
# An abstracted remote mutator for setting Asterisk variables.
|
108
|
+
def set_variable(key,value) rawr "SET VARIABLE #{key} #{value}" end
|
109
|
+
|
110
|
+
# An abstracted remote accessor for retrieving Asterisk variables.
|
111
|
+
def get_variable(key, default=nil)
|
112
|
+
result = rawr "GET VARIABLE #{key}"
|
113
|
+
result[0..12] == "200 result=0" ? default : result[13..-1]
|
114
|
+
end
|
115
|
+
|
116
|
+
def dial who, *options
|
117
|
+
#group = who.group if who.respond_to :group
|
118
|
+
exec :dial, properize(who)
|
119
|
+
end
|
120
|
+
|
121
|
+
# The stream_file() method comes right over from the AGI protocol.
|
122
|
+
# Its use is very similar to the Background() application from
|
123
|
+
# Asterisk's extensions.conf language. A file is played in the
|
124
|
+
# background while optionally waiting for input. In the event
|
125
|
+
# the second argument is given, Asterisk will listen for that
|
126
|
+
# sequence of digits and return control to Adhearsion, informing
|
127
|
+
# us of the digits the user pressed. These digits are returned
|
128
|
+
# as a String. If you would also like to know the ending position
|
129
|
+
# at which the streaming stopped, see stream_file_with_offset().
|
130
|
+
def stream_file file, digits='', offset=0
|
131
|
+
stream_file_with_offset(file, digits, offset).first
|
132
|
+
end
|
133
|
+
|
134
|
+
# Similar to stream_file(), but returning an array of length 2
|
135
|
+
# instead. If supplied a first argument, the return value will
|
136
|
+
# be digits pressed by the user to end
|
137
|
+
def stream_file_with_offset file, digits='', offset=0
|
138
|
+
response = rawr "STREAM FILE #{file} #{digits} #{offset}"
|
139
|
+
return nil unless response.starts_with? '200'
|
140
|
+
|
141
|
+
result_index = response.index(?=) + 1
|
142
|
+
spacer_index = response.rindex(' ')
|
143
|
+
endpos_index = spacer_index + 8 # " endpos=".length == 8
|
144
|
+
|
145
|
+
result = response[result_index...spacer_index].to_i
|
146
|
+
endpos = response[endpos_index..-1].to_i
|
147
|
+
|
148
|
+
return [nil,nil] if (result.zero? && endpos.zero?) || result == -1
|
149
|
+
|
150
|
+
[result, endpos]
|
151
|
+
end
|
152
|
+
|
153
|
+
# Festival is pretty buggy (and pretty inefficient). In theory,
|
154
|
+
# this should speak out any text supplied to it.
|
155
|
+
def speak text
|
156
|
+
text.gsub!('\n').gsub!('\r').strip!
|
157
|
+
exec :festival, "'#{text}'"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Since voicemail is used so frequently, extra effort has been made to
|
161
|
+
# make it syntactically sweet. voicemail() takes the mailbox number of
|
162
|
+
# a user as its first or second argument and the mailbox type as its
|
163
|
+
# first or second argument (the order doesn't matter). If a specific
|
164
|
+
# voicemail context is necessary, trail it to the end of the method
|
165
|
+
# with the ":at => 'contextname'" syntax. The mailbox type can be
|
166
|
+
# :busy, :unavailable, or :normal, though if no type is given, the
|
167
|
+
# type :normal is assumed.
|
168
|
+
#
|
169
|
+
# Usage: voicemail :busy, 4544
|
170
|
+
# or: voicemail 4544
|
171
|
+
# or: voicemail 4544, :unavailable, :at => 'codemecca'
|
172
|
+
def voicemail msg_or_who=nil, who_or_msg=nil, hash={}
|
173
|
+
opts = [msg_or_who, who_or_msg]
|
174
|
+
type = opts & [:normal, :busy, :unavailable]
|
175
|
+
box = opts - type
|
176
|
+
box.last <<= "@#{hash[:at]}}" if hash[:at]
|
177
|
+
exec :Voicemail, *(type + box)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Execute VoiceMailMain behind the scenes to check a specified
|
181
|
+
# user's voicemail inbox or, if no arguments are passed, prompt
|
182
|
+
# the user for their mailbox number.
|
183
|
+
#
|
184
|
+
# Usage: check_voicemail 'jay', :at => 'default', :args => 's'
|
185
|
+
# or : check_voicemail
|
186
|
+
# or : check_voicemail 'hubbard'
|
187
|
+
def check_voicemail name=nil, hash={}
|
188
|
+
args = [ [name, hash[:at]].compact * '@', hash[:args]]
|
189
|
+
exec :VoiceMailMain, args.compact
|
190
|
+
end
|
191
|
+
|
192
|
+
# Play is a long-overdue easy way to play audio files in a dialplan with Asterisk. It can take a
|
193
|
+
# single String of the filename (defaults to /var/lib/asterisk/sounds) just as you
|
194
|
+
# would give it to Playback(), or it can take any number of trailed on Strings.
|
195
|
+
# If you pass it an Array, it will traverse the array, playing each of the items
|
196
|
+
# in order. If you're doing this, make your life *incredibly* easier by using the
|
197
|
+
# fantastic Ruby Array literal for Arrays of Strings: %w(). Within this, one
|
198
|
+
# specifies separate, simple Strings by placing whitespace bewtween them. Since
|
199
|
+
# no Asterisk files contain whitespace, this works _very_ well! Example:
|
200
|
+
#
|
201
|
+
# play %w(a-connect-charge-of 22 cents-per-minute will-apply)
|
202
|
+
#
|
203
|
+
# Note here that numbers can be play()ed as well. By convention, play() will call
|
204
|
+
# SayNumber() on these indices instead of Playback().
|
205
|
+
def play *files
|
206
|
+
files.flatten!
|
207
|
+
files.each do |f|
|
208
|
+
if f.simplify.is_a? Fixnum then exec :saynumber, f
|
209
|
+
else exec :playback, f
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# The rawr() method is the main way of receiving a raw response from the Asterisk
|
215
|
+
# server. When no argument is given, it will immediately ask for a response,
|
216
|
+
# returning that String. When an argument +is+ given, it will first send that
|
217
|
+
# command and then return the response that command generated. Everything is
|
218
|
+
# chomp()ed before returned. Pun here totally intended.
|
219
|
+
def rawr(what=nil)
|
220
|
+
begin
|
221
|
+
putc what if what
|
222
|
+
PBX.io.gets.chomp!
|
223
|
+
rescue => e
|
224
|
+
#log "Socket no longer available for communication. Exceptions will likely occur."
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Simply print()s the command over the AGI IO socket.
|
229
|
+
def putc(what) PBX.io.print what end
|
230
|
+
|
231
|
+
# The magical method that handles how objects passed to dial() are converted to their
|
232
|
+
# corresponding Asterisk-recognizable technology/extension identifier. Likely wouldn't
|
233
|
+
# be used much outside of dial().
|
234
|
+
def properize who
|
235
|
+
possible_methods = [:users, :members, :user, :member].select { |pm| who.respond_to? pm } # These are the convention methods for Group-like objects
|
236
|
+
who = who.send possible_methods.first if possible_methods.any? # If 'who' has any of the possible_methods, replace 'who' with the return value of that method
|
237
|
+
return unless who
|
238
|
+
who = [who] unless who.kind_of?(Enumerable) && !who.kind_of?(String) # In case we can't perform collection algorithms on 'who', let's encapsulate it in an Array
|
239
|
+
# If the first thing in the Enumerable responds to a User-like convention, then set 'who' equal to the extension accessor of those objects
|
240
|
+
who.map! { |p| p.send possible_methods.first }.compact! if (possible_methods = [:extension, :extensions].select { |pm| who.first.respond_to? pm }).any?
|
241
|
+
# Now replace each item in the Enumerable with its form converted to extension (assuming it's not already in that format)
|
242
|
+
who.map! do |ext|
|
243
|
+
if ext.kind_of?(String) && ext.index(?/) then ext
|
244
|
+
else
|
245
|
+
ext = "1#{ext}" if ext.is_national_number? && ext.to_s[0] != ?1
|
246
|
+
"SIP/#{ext}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
who *= '&' # Finally, join() anything left in the Array with an '&'
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns the status of the last dial(). Possible dial
|
253
|
+
# statuses include :answer, :busy, :noanswer, :cancel,
|
254
|
+
# :congestion, and :chanunavail. If :cancel is
|
255
|
+
# returned, the caller hung up before the callee picked
|
256
|
+
# up. If :congestion is returned, the dialed extension
|
257
|
+
# probably doesn't exist. If :chanunavail, the callee
|
258
|
+
# phone may not be registered.
|
259
|
+
def last_dial_status
|
260
|
+
get_variable(:DIALSTATUS).downcase.to_sym
|
261
|
+
end
|
262
|
+
|
263
|
+
# Answer the channel. Adhearsion is configured by default to automatically do this
|
264
|
+
# when a call comes in.
|
265
|
+
def answer() rawr 'ANSWER' end
|
266
|
+
# Hangs up the channel. Adhearsion is configured by default to matically do this
|
267
|
+
# when a context completes execution
|
268
|
+
def hangup() rawr 'HANGUP' end
|
269
|
+
# Direct translation of the Asterisk NoOp() application. Used primarily for
|
270
|
+
# viewing debug information in the Asterisk CLI.
|
271
|
+
def noop(*options) rawr "NOOP #{options}" end
|
272
|
+
|
273
|
+
# The SIP/ZAP/IAX/IAX2/Zap methods allow for cleaner addressing of particular
|
274
|
+
# extensions, abstracting Asterisk's representation. If not given any
|
275
|
+
# argument, this methods simply return a symbol identifying their
|
276
|
+
# appropriate protocol.
|
277
|
+
|
278
|
+
# See the SIP class
|
279
|
+
def SIP(ext=nil) ext ? "SIP/#{ext}" : :SIP end
|
280
|
+
# See the ZAP class
|
281
|
+
def ZAP(ext=nil) ext ? "Zap/#{ext}" : :ZAP end
|
282
|
+
# See the IAX class
|
283
|
+
def IAX(ext=nil) ext ? "IAX2/#{ext}" : :IAX end
|
284
|
+
alias IAX2 IAX
|
285
|
+
alias Zap ZAP
|
286
|
+
|
287
|
+
# For the completely selfish purpose of sugaring the syntax, two tiny holder classes
|
288
|
+
# are created to allow the synonym of SIP(1404) to be SIP[1404]. Both functions,
|
289
|
+
# when given an argument, return a String that Asterisk understands representing that
|
290
|
+
# particular channel.
|
291
|
+
|
292
|
+
# Syntax sugar for declaring SIP devices. Use: SIP/123 or SIP[123]
|
293
|
+
class SIP
|
294
|
+
def self.[](arg=nil) SIP(arg) end
|
295
|
+
def self./(arg=nil) SIP(arg) end
|
296
|
+
end
|
297
|
+
# Syntax sugar for declaring IAX devices. Use: IAX/123 or IAX[123]
|
298
|
+
class IAX
|
299
|
+
def self.[](arg=nil) IAX(arg) end
|
300
|
+
def self./(arg=nil) IAX(arg) end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Syntax sugar for declaring ZAP devices. Use: ZAP/123 or ZAP[123]
|
304
|
+
class ZAP
|
305
|
+
def self.[](arg=nil) ZAP(arg) end
|
306
|
+
def self./(arg=nil) ZAP(arg) end
|
307
|
+
end
|
308
|
+
|
309
|
+
# For users who may try to use the (proper) IAX2 form. See IAX for more info.
|
310
|
+
class IAX2 < IAX; end
|
311
|
+
|
312
|
+
# For users who may try to use the (common) "Zap" form. See ZAP for more info.
|
313
|
+
class Zap < ZAP; end
|
314
|
+
end
|
315
|
+
|
316
|
+
# The PBX object is an object manifestation of the PBX with which Adhearsion will be
|
317
|
+
# associated. Helpers often open up this class and add methods to it. Note: when doing
|
318
|
+
# this in your own helpers, all methods will need to be class (a.k.a. static) methods
|
319
|
+
# since no actual instance of this object is passed around.
|
320
|
+
class PBX
|
321
|
+
def PBX.io() Thread.current[:io] end
|
322
|
+
end
|
323
|
+
|
324
|
+
# The Contexts class is a blank slate in which the extensions.rb file is evaluated.
|
325
|
+
# Its method_missing() simply takes a block and, given the name of attempted method,
|
326
|
+
# meta_def()s a new accessor method in Contexts with this name that returns the block
|
327
|
+
# given. This is how contexts can be included anywhere in extensions.rb using the
|
328
|
+
# easy "+context_name" syntax.
|
329
|
+
class Contexts
|
330
|
+
|
331
|
+
(instance_methods - %w(__send__ __id__ define_method instance_eval)).each do |m|
|
332
|
+
undef_method m
|
333
|
+
end
|
334
|
+
|
335
|
+
def method_missing name, *args, &block
|
336
|
+
super(name, *args, &block) unless block
|
337
|
+
Thread.current[:container].run_inside do
|
338
|
+
meta_def name do
|
339
|
+
block
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# This Container object is a container in which each context is executed (not instantiated).
|
345
|
+
# It extends the Asterisk module from adhearsion.rb and thus inherits all of the functionality
|
346
|
+
# declared there. This class can be monkey patched if necessary.
|
347
|
+
class Container
|
348
|
+
include Asterisk
|
349
|
+
def initialize
|
350
|
+
class << self
|
351
|
+
def metaclass; class << self; self; end; end
|
352
|
+
def meta_eval &blk; metaclass.instance_eval(&blk); end
|
353
|
+
def meta_def name, &blk
|
354
|
+
meta_eval { define_method name, &blk }
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def method_missing name, *args, &block
|
360
|
+
Kernel.method_missing name, *args, &block
|
361
|
+
end
|
362
|
+
|
363
|
+
def run_inside &code
|
364
|
+
instance_eval(&code)
|
365
|
+
end
|
366
|
+
|
367
|
+
def eval_inside code
|
368
|
+
run_inside do
|
369
|
+
eval code
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# An Exception thrown when the directory supplied in the constructor of a
|
376
|
+
# new RailsApp object is invalid.
|
377
|
+
class InvalidRailsDirectory < Exception;end
|
378
|
+
|
379
|
+
# When instantiated with an absolute location to a Rails app, the new RailsApp will perform
|
380
|
+
# a number of useful observations about the files and directories available, such as loading
|
381
|
+
# the database configuration and listing all of the models. All observations are made accessible
|
382
|
+
# through attribute accessors.
|
383
|
+
class RailsApp
|
384
|
+
def initialize path=Dir.pwd
|
385
|
+
update! path
|
386
|
+
end
|
387
|
+
|
388
|
+
# Performs the observations on the Rails app once more.
|
389
|
+
def update! path
|
390
|
+
@path = path
|
391
|
+
@database_config_file = File.join @path, 'config', 'database.yml'
|
392
|
+
if File.readable? @database_config_file
|
393
|
+
@database_config = YAML.load_file @database_config_file
|
394
|
+
else raise InvalidRailsDirectory.new("Database config file #{@database_config_file} not found!")
|
395
|
+
end
|
396
|
+
@models_folder = File.join path, 'app', 'models'
|
397
|
+
@models_files = Dir[ File.join(@models_folder, '*.rb') ]
|
398
|
+
@time_updated = Time.now
|
399
|
+
end
|
400
|
+
attr_reader :database_config_file, :database_config, :path, :models_folder, :models_files,
|
401
|
+
:models_names, :time_updated
|
402
|
+
end
|
data/lib/constants.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Adhearsion, open source technology integrator
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
# Please help adjust these if they may be inaccurate!
|
19
|
+
LOCAL_NUMBER = /^[1-9]\d{6}$/
|
20
|
+
US_NUMBER = /^1?[1-9]\d{2})?[1-9]\d{6}$/
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# Adhearsion, open source technology integrator
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'active_support'
|
19
|
+
|
20
|
+
class Object
|
21
|
+
|
22
|
+
# For dealing with data that may be more difficult to manage than it should be.
|
23
|
+
# Strings with simplify() called on them return integers if they appear to be
|
24
|
+
# integers. Arrays with only one item in it return ary.first.simplify.
|
25
|
+
def simplify
|
26
|
+
if is_a? String
|
27
|
+
return Integer(self) if self =~ /^\d+$/
|
28
|
+
elsif is_a? Array
|
29
|
+
return first.simplify if size == 1
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
def is_a_group?
|
34
|
+
# TODO: check if it responds to the conventions.
|
35
|
+
end
|
36
|
+
def is_a_user?
|
37
|
+
# TODO: check if it responds to the conventions.
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_local_number?
|
41
|
+
to_s =~ LOCAL_NUMBER
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_national_number?
|
45
|
+
to_s =~ national_number
|
46
|
+
end
|
47
|
+
|
48
|
+
def mutex() @mutex ||= Mutex.new end
|
49
|
+
def synchronize() mutex.synchronize { yield self } end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
$BEFORE_CALL, $BEFORE_CALL_HIGH, $BEFORE_CALL_LOW = [],[],[]
|
54
|
+
def before_call priority=:normal, &block
|
55
|
+
name = priority == :normal ? "BEFORE_CALL" : "BEFORE_CALL_#{priority.to_s.upcase}"
|
56
|
+
eval("$#{name}") << block
|
57
|
+
end
|
58
|
+
|
59
|
+
$AFTER_CALL, $AFTER_CALL_HIGH, $AFTER_CALL_LOW = [],[],[]
|
60
|
+
def after_call priority=:normal, &block
|
61
|
+
name = priority == :normal ? "AFTER_CALL" : "AFTER_CALL_#{priority.to_s.upcase}"
|
62
|
+
eval("$#{name}") << block
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class String
|
67
|
+
|
68
|
+
def String.random_char
|
69
|
+
case r = rand(62)
|
70
|
+
when 0...10 then r.to_s
|
71
|
+
when 10...36 then (r+55).chr
|
72
|
+
when 36...62 then (r+61).chr
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Handy way to generate random strings of an arbitrary lengths.
|
77
|
+
def String.random len=8
|
78
|
+
str = ''
|
79
|
+
len.times do str << String.random_char end
|
80
|
+
str
|
81
|
+
end
|
82
|
+
def nameify() downcase.gsub(/[^\w]/, '') end
|
83
|
+
def nameify!() replace nameify end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Proc
|
87
|
+
def +@
|
88
|
+
Thread.current[:container].run_inside(&self)
|
89
|
+
true # Allows "and" operator
|
90
|
+
end
|
91
|
+
|
92
|
+
def ~@
|
93
|
+
raise ControlPassingException.new(self)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# A ControlPassingException is used internally to stop execution of one
|
99
|
+
# dialplan context and begin execution of another proc instead. It is
|
100
|
+
# most notably used by the ~@ unary operator that can be called on a
|
101
|
+
# context name within a dialplan to transfer control entirely to that
|
102
|
+
# particular context. The serve() method in the servlet_container actually
|
103
|
+
# rescues these exceptions specifically and then does +e.target to execute
|
104
|
+
# that code.
|
105
|
+
class ControlPassingException < Exception
|
106
|
+
def initialize target
|
107
|
+
@target = target
|
108
|
+
super
|
109
|
+
end
|
110
|
+
attr_reader :target
|
111
|
+
end
|
112
|
+
|
113
|
+
#class Regexp
|
114
|
+
# alias replaced_triple_equals ===
|
115
|
+
# # Allows regular expressions to be compared against numbers in case statements.
|
116
|
+
# def === other
|
117
|
+
# # This feature is so valuable to the dialplan DSL that a little bit
|
118
|
+
# # of hackery is justified.
|
119
|
+
#
|
120
|
+
# if caller.find {|i| i.ends_with? ":in `run_inside'" } # Test if running in the DSL
|
121
|
+
# return replaced_triple_equals(other.to_s) if other.kind_of? Numeric
|
122
|
+
# end
|
123
|
+
# replaced_triple_equals other
|
124
|
+
# end
|
125
|
+
#end
|
126
|
+
|
127
|
+
class Fixnum
|
128
|
+
|
129
|
+
# One ring is approximately six seconds.
|
130
|
+
def rings() self*6 end
|
131
|
+
alias second seconds
|
132
|
+
alias digit seconds
|
133
|
+
alias digits seconds
|
134
|
+
def =~ other
|
135
|
+
to_s =~ other
|
136
|
+
end
|
137
|
+
|
138
|
+
# Used by __case__ statements. For Adhearsion's purposes, passing
|
139
|
+
# a Fixnum to a case statement will convert both the Fixnum operand
|
140
|
+
# and other operand to Strings and then return their === equivalency.
|
141
|
+
def === other
|
142
|
+
to_s === other.to_s
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Hash
|
147
|
+
def method_missing name, *args
|
148
|
+
if name.to_s[-1] == ?= then self[name.to_s.chop.to_sym] = args.first
|
149
|
+
else self[name] || self[name.to_s]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class Array
|
155
|
+
def first=(value) self[0] = value end
|
156
|
+
def last=(value) self[-1] = value end
|
157
|
+
end
|