adhearsion 0.7.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.
- 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
|