rene-adhearsion 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/CHANGELOG +73 -0
  2. data/EVENTS +11 -0
  3. data/LICENSE +456 -0
  4. data/Rakefile +130 -0
  5. data/adhearsion.gemspec +173 -0
  6. data/app_generators/ahn/USAGE +5 -0
  7. data/app_generators/ahn/ahn_generator.rb +96 -0
  8. data/app_generators/ahn/templates/.ahnrc +34 -0
  9. data/app_generators/ahn/templates/README +8 -0
  10. data/app_generators/ahn/templates/Rakefile +25 -0
  11. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  12. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  13. data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
  14. data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
  15. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
  16. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
  17. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +263 -0
  18. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
  19. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
  20. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  21. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  22. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
  23. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
  24. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
  25. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  26. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  27. data/app_generators/ahn/templates/config/startup.rb +83 -0
  28. data/app_generators/ahn/templates/dialplan.rb +3 -0
  29. data/app_generators/ahn/templates/events.rb +32 -0
  30. data/bin/ahn +28 -0
  31. data/bin/ahnctl +68 -0
  32. data/bin/jahn +42 -0
  33. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  34. data/lib/adhearsion.rb +45 -0
  35. data/lib/adhearsion/cli.rb +228 -0
  36. data/lib/adhearsion/component_manager.rb +272 -0
  37. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  38. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  39. data/lib/adhearsion/events_support.rb +84 -0
  40. data/lib/adhearsion/foundation/all.rb +15 -0
  41. data/lib/adhearsion/foundation/blank_slate.rb +3 -0
  42. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  43. data/lib/adhearsion/foundation/event_socket.rb +204 -0
  44. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  45. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  46. data/lib/adhearsion/foundation/numeric.rb +13 -0
  47. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  48. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  49. data/lib/adhearsion/foundation/string.rb +26 -0
  50. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  51. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  52. data/lib/adhearsion/host_definitions.rb +67 -0
  53. data/lib/adhearsion/initializer.rb +395 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +87 -0
  55. data/lib/adhearsion/initializer/configuration.rb +321 -0
  56. data/lib/adhearsion/initializer/database.rb +60 -0
  57. data/lib/adhearsion/initializer/drb.rb +31 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/ldap.rb +57 -0
  60. data/lib/adhearsion/initializer/rails.rb +41 -0
  61. data/lib/adhearsion/initializer/xmpp.rb +42 -0
  62. data/lib/adhearsion/logging.rb +92 -0
  63. data/lib/adhearsion/tasks.rb +16 -0
  64. data/lib/adhearsion/tasks/database.rb +5 -0
  65. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  66. data/lib/adhearsion/tasks/generating.rb +20 -0
  67. data/lib/adhearsion/tasks/lint.rb +4 -0
  68. data/lib/adhearsion/tasks/testing.rb +37 -0
  69. data/lib/adhearsion/version.rb +33 -0
  70. data/lib/adhearsion/voip/asterisk.rb +4 -0
  71. data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
  72. data/lib/adhearsion/voip/asterisk/commands.rb +1510 -0
  73. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  74. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  75. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  76. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  77. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  78. data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
  79. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1680 -0
  80. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +340 -0
  81. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  82. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  83. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  84. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  85. data/lib/adhearsion/voip/call.rb +497 -0
  86. data/lib/adhearsion/voip/call_routing.rb +64 -0
  87. data/lib/adhearsion/voip/commands.rb +9 -0
  88. data/lib/adhearsion/voip/constants.rb +39 -0
  89. data/lib/adhearsion/voip/conveniences.rb +18 -0
  90. data/lib/adhearsion/voip/dial_plan.rb +246 -0
  91. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  92. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  93. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  94. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
  96. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  97. data/lib/adhearsion/voip/dsl/numerical_string.rb +115 -0
  98. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  99. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  100. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  101. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  102. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  103. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  104. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  105. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  106. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  107. data/lib/adhearsion/xmpp/connection.rb +61 -0
  108. data/lib/theatre.rb +151 -0
  109. data/lib/theatre/README.markdown +64 -0
  110. data/lib/theatre/callback_definition_loader.rb +84 -0
  111. data/lib/theatre/guid.rb +23 -0
  112. data/lib/theatre/invocation.rb +121 -0
  113. data/lib/theatre/namespace_manager.rb +153 -0
  114. data/lib/theatre/version.rb +2 -0
  115. metadata +241 -0
@@ -0,0 +1,87 @@
1
+ require 'adhearsion/voip/asterisk'
2
+ module Adhearsion
3
+ class Initializer
4
+
5
+ class AsteriskInitializer
6
+
7
+ cattr_accessor :config, :agi_server, :ami_client
8
+ class << self
9
+
10
+ def start
11
+ self.config = AHN_CONFIG.asterisk
12
+ self.agi_server = initialize_agi
13
+ self.ami_client = VoIP::Asterisk.manager_interface = initialize_ami if config.ami_enabled?
14
+ join_server_thread_after_initialized
15
+
16
+ # Make sure we stop everything when we shutdown
17
+ Events.register_callback(:shutdown) do
18
+ ahn_log.info "Shutting down with #{Adhearsion.active_calls.size} active calls"
19
+ self.stop
20
+ end
21
+ end
22
+
23
+ def stop
24
+ agi_server.graceful_shutdown
25
+ ami_client.disconnect! if ami_client
26
+ end
27
+
28
+ private
29
+
30
+ def initialize_agi
31
+ VoIP::Asterisk::AGI::Server.new :host => config.listening_host,
32
+ :port => config.listening_port
33
+ end
34
+
35
+ def initialize_ami
36
+ options = ami_options
37
+ start_ami_after_initialized
38
+ VoIP::Asterisk::Manager::ManagerInterface.new(options).tap do
39
+ class << VoIP::Asterisk
40
+ if respond_to?(:manager_interface)
41
+ ahn_log.warn "Asterisk.manager_interface already initialized?"
42
+ else
43
+ def manager_interface
44
+ # ahn_log.ami.warn "Warning! This Asterisk.manager_interface() notation is for Adhearsion version 0.8.0 only. Subsequent versions of Adhearsion will use a feature called SuperManager. Migrating to use SuperManager will be very simple. See http://docs.adhearsion.com/AMI for more information."
45
+ Adhearsion::Initializer::AsteriskInitializer.ami_client
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def ami_options
53
+ %w(host port username password events auto_reconnect).inject({}) do |options, property|
54
+ options[property.to_sym] = config.ami.send property
55
+ options
56
+ end
57
+ end
58
+
59
+ def join_server_thread_after_initialized
60
+ Events.register_callback(:after_initialized) do
61
+ begin
62
+ agi_server.start
63
+ rescue => e
64
+ ahn_log.fatal "Failed to start AGI server! #{e.inspect}"
65
+ abort
66
+ end
67
+ end
68
+ IMPORTANT_THREADS << agi_server
69
+ end
70
+
71
+ def start_ami_after_initialized
72
+ Events.register_callback(:after_initialized) do
73
+ begin
74
+ self.ami_client.connect!
75
+ rescue Errno::ECONNREFUSED
76
+ ahn_log.ami.error "Connection refused when connecting to AMI! Please check your configuration."
77
+ rescue => e
78
+ ahn_log.ami.error "Error connecting to AMI! #{e.inspect}"
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,321 @@
1
+ module Adhearsion
2
+ class Configuration
3
+ module ConfigurationEntryPoint
4
+ def add_configuration_for(name)
5
+ configuration_class_name = "#{name}Configuration"
6
+ lowercased_name = name.to_s.underscore
7
+
8
+ class_eval(<<-EVAL, __FILE__, __LINE__)
9
+ def enable_#{lowercased_name}(configuration_options = {})
10
+ @#{lowercased_name}_configuration = #{configuration_class_name}.new(configuration_options)
11
+ end
12
+
13
+ def #{lowercased_name}
14
+ @#{lowercased_name}_configuration
15
+ end
16
+
17
+ def #{lowercased_name}_enabled?
18
+ !#{lowercased_name}.nil?
19
+ end
20
+ EVAL
21
+ end
22
+ end
23
+ extend ConfigurationEntryPoint
24
+
25
+ class << self
26
+ def configure(&block)
27
+ if Adhearsion.const_defined?(:AHN_CONFIG)
28
+ yield AHN_CONFIG if block_given?
29
+ else
30
+ Adhearsion.const_set(:AHN_CONFIG, new(&block))
31
+ end
32
+ end
33
+ end
34
+
35
+ attr_accessor :automatically_answer_incoming_calls
36
+ attr_accessor :end_call_on_hangup
37
+ attr_accessor :end_call_on_error
38
+ attr_accessor :components_to_load
39
+
40
+ def initialize
41
+ @automatically_answer_incoming_calls = true
42
+ @end_call_on_hangup = true
43
+ @end_call_on_error = true
44
+ @components_to_load = []
45
+ yield self if block_given?
46
+ end
47
+
48
+ def ahnrc
49
+ @ahnrc
50
+ end
51
+
52
+ ##
53
+ # Load the contents of an .ahnrc file into this Configuration.
54
+ #
55
+ # @param [String, Hash] ahnrc String of YAML .ahnrc data or a Hash of the pre-loaded YAML data structure
56
+ #
57
+ def ahnrc=(new_ahnrc)
58
+ case new_ahnrc
59
+ when Hash
60
+ @raw_ahnrc = new_ahnrc.to_yaml.freeze
61
+ @ahnrc = new_ahnrc.clone.freeze
62
+ when String
63
+ @raw_ahnrc = new_ahnrc.clone.freeze
64
+ @ahnrc = YAML.load(new_ahnrc).freeze
65
+ end
66
+ end
67
+
68
+ def logging(options)
69
+ Adhearsion::Logging.logging_level = options[:level]
70
+ end
71
+
72
+ def add_component(*list)
73
+ AHN_CONFIG.components_to_load |= list
74
+ end
75
+
76
+ ##
77
+ # Adhearsion's .ahnrc file is used to define paths to certain parts of the framework. For example, the name dialplan.rb
78
+ # is actually specified in .ahnrc. This file can actually be just a filename, a filename with a glob (.e.g "*.rb"), an
79
+ # Array of filenames or even an Array of globs.
80
+ #
81
+ # @param [String,Array] String segments which convey the nesting of Hash keys through .ahnrc
82
+ # @raise [RuntimeError] If ahnrc has not been set yet with #ahnrc=()
83
+ # @raise [NameError] If the path through the ahnrc is invalid
84
+ #
85
+ def files_from_setting(*path_through_config)
86
+ raise RuntimeError, "No ahnrc has been set yet!" unless @ahnrc
87
+ queried_nested_setting = path_through_config.flatten.inject(@ahnrc) do |hash,key_name|
88
+ if hash.kind_of?(Hash) && hash.has_key?(key_name)
89
+ hash[key_name]
90
+ else
91
+ raise NameError, "Paths #{path_through_config.inspect} not found in .ahnrc!"
92
+ end
93
+ end
94
+ raise NameError, "Paths #{path_through_config.inspect} not found in .ahnrc!" unless queried_nested_setting
95
+ queried_nested_setting = Array queried_nested_setting
96
+ queried_nested_setting.map { |filename| files_from_glob(filename) }.flatten.uniq
97
+ end
98
+
99
+ private
100
+
101
+ def files_from_glob(glob)
102
+ Dir.glob "#{AHN_ROOT}/#{glob}"
103
+ end
104
+
105
+ class AbstractConfiguration
106
+ extend ConfigurationEntryPoint
107
+
108
+ class << self
109
+ private
110
+ def abstract_method!
111
+ raise "Must be implemented in subclasses"
112
+ end
113
+ end
114
+
115
+ def initialize(overrides = {})
116
+ overrides.each_pair do |attribute, value|
117
+ send("#{attribute}=", value)
118
+ end
119
+ end
120
+ end
121
+
122
+ # Abstract superclass for AsteriskConfiguration and FreeSwitchConfiguration.
123
+ class TelephonyPlatformConfiguration < AbstractConfiguration
124
+ attr_accessor :listening_port
125
+ attr_accessor :listening_host
126
+
127
+ class << self
128
+ def default_listening_port
129
+ abstract_method!
130
+ end
131
+
132
+ def default_listening_host
133
+ '0.0.0.0'
134
+ end
135
+ end
136
+
137
+ def initialize(overrides = {})
138
+ @listening_host = overrides.has_key?(:host) ? overrides.delete(:host) : self.class.default_listening_host
139
+ @listening_port = overrides.has_key?(:port) ? overrides.delete(:port) : self.class.default_listening_port
140
+ super
141
+ end
142
+ end
143
+
144
+ class AsteriskConfiguration < TelephonyPlatformConfiguration
145
+ attr_accessor :speech_engine
146
+ attr_accessor :argument_delimiter
147
+
148
+ class << self
149
+ def default_listening_port
150
+ 4573
151
+ end
152
+
153
+ # Keep Asterisk 1.4 (and prior) as the default to protect upgraders
154
+ # This setting only applies to AGI. AMI delimiters are always
155
+ # auto-detected.
156
+ def default_argument_delimiter
157
+ '|'
158
+ end
159
+ end
160
+
161
+ def initialize(overrides = {})
162
+ @argument_delimiter = self.class.default_argument_delimiter
163
+ super
164
+ end
165
+
166
+ class AMIConfiguration < AbstractConfiguration
167
+ attr_accessor :port, :username, :password, :events, :host, :auto_reconnect
168
+
169
+ class << self
170
+ def default_port
171
+ 5038
172
+ end
173
+
174
+ def default_events
175
+ false
176
+ end
177
+
178
+ def default_host
179
+ 'localhost'
180
+ end
181
+
182
+ def default_auto_reconnect
183
+ true
184
+ end
185
+ end
186
+
187
+ def initialize(overrides = {})
188
+ self.host = self.class.default_host
189
+ self.port = self.class.default_port
190
+ self.events = self.class.default_events
191
+ self.auto_reconnect = self.class.default_auto_reconnect
192
+ super
193
+ end
194
+ end
195
+ add_configuration_for :AMI
196
+ end
197
+ add_configuration_for :Asterisk
198
+
199
+ class FreeswitchConfiguration < TelephonyPlatformConfiguration
200
+ class << self
201
+ def default_listening_port
202
+ 4572
203
+ end
204
+ end
205
+ end
206
+ add_configuration_for :Freeswitch
207
+
208
+ class DatabaseConfiguration < AbstractConfiguration
209
+ attr_accessor :connection_options, :orm
210
+ def initialize(options)
211
+ @orm = options.delete(:orm) || :active_record # TODO: ORM is a misnomer
212
+ @connection_options = options
213
+ end
214
+ end
215
+ add_configuration_for :Database
216
+
217
+ class LdapConfiguration < AbstractConfiguration
218
+ attr_accessor :connection_options
219
+ def initialize(options)
220
+ @connection_options = options
221
+ end
222
+ end
223
+ add_configuration_for :Ldap
224
+
225
+ class DrbConfiguration < AbstractConfiguration
226
+ attr_accessor :port
227
+ attr_accessor :host
228
+ attr_accessor :acl
229
+
230
+ # ACL = Access Control List
231
+
232
+ class << self
233
+ def default_port
234
+ 9050
235
+ end
236
+
237
+ def default_host
238
+ 'localhost'
239
+ end
240
+ end
241
+
242
+ def initialize(overrides = {})
243
+ self.port = overrides[:port] || self.class.default_port
244
+ self.host = overrides[:host] || self.class.default_host
245
+ self.acl = overrides[:raw_acl]
246
+
247
+ unless acl
248
+ self.acl = []
249
+ overrides[ :deny].to_a.each { |ip| acl << 'deny' << ip }
250
+ overrides[:allow].to_a.each { |ip| acl << 'allow' << ip }
251
+ acl.concat %w[allow 127.0.0.1] if acl.empty?
252
+ end
253
+ end
254
+ end
255
+ add_configuration_for :Drb
256
+
257
+ class RailsConfiguration < AbstractConfiguration
258
+
259
+ attr_accessor :rails_root, :environment
260
+ def initialize(options)
261
+ path_to_rails, environment = check_options options
262
+ @rails_root = File.expand_path(path_to_rails)
263
+ @environment = environment.to_sym
264
+ end
265
+
266
+ private
267
+
268
+ def check_options(options)
269
+ options = options.clone
270
+ path = options.delete :path
271
+ env = options.delete :env
272
+ raise ArgumentError, "Unrecognied argument(s) #{options.keys.to_sentence} in Rails initializer!" unless options.size.zero?
273
+ raise ArgumentError, "Must supply an :env argument to the Rails initializer!" unless env
274
+ raise ArgumentError, "Must supply an :path argument to the Rails initializer!" unless path
275
+ [path, env]
276
+ end
277
+
278
+ end
279
+ add_configuration_for :Rails
280
+
281
+ class XMPPConfiguration < AbstractConfiguration
282
+
283
+ attr_accessor :jid, :password, :server, :port
284
+ def initialize(options)
285
+ jid, password, server, port = check_options options
286
+ @jid = jid
287
+ @password = password
288
+ @server = server
289
+ @port = port
290
+ end
291
+
292
+ class << self
293
+ def default_port
294
+ 5222
295
+ end
296
+ end
297
+
298
+ private
299
+
300
+ def check_options(options)
301
+ options = options.clone
302
+ jid = options.delete :jid
303
+ password = options.delete :password
304
+ server = options.delete :server
305
+ port = options.delete :port
306
+ raise ArgumentError, "Unrecognied argument(s) #{options.keys.to_sentence} in XMPP initializer!" unless options.size.zero?
307
+ raise ArgumentError, "Must supply a :jid argument to the XMPP initializer!" unless jid
308
+ raise ArgumentError, "Must supply a :password argument to the XMPP initializer!" unless password
309
+ if server
310
+ port ||= self.class.default_port
311
+ else
312
+ raise ArgumentError, "Must supply a :server argument as well as :port to the XMPP initializer!" if port
313
+ end
314
+ [jid, password, server, port]
315
+ end
316
+
317
+ end
318
+ add_configuration_for :XMPP
319
+
320
+ end
321
+ end
@@ -0,0 +1,60 @@
1
+ # TODO: Have all of the initializer modules required and then traverse the subclasses, asking them if they're enabled. If they are enabled, then they should do their initialization stuff. Is this really necessary to develop this entirely new system when the components system exists?
2
+
3
+ module Adhearsion
4
+ class Initializer
5
+
6
+ class DatabaseInitializer
7
+
8
+ class << self
9
+
10
+ def start
11
+ require_dependencies
12
+ require_models
13
+ @@config = Adhearsion::AHN_CONFIG.database
14
+ # You may need to uncomment the following line for older versions of ActiveRecord
15
+ # ActiveRecord::Base.allow_concurrency = true
16
+ establish_connection
17
+ ActiveRecord::Base.logger =
18
+ @@config.connection_options.has_key?(:logger) ?
19
+ @@config.connection_options[:logger] :
20
+ ahn_log.db
21
+ create_call_hook_for_connection_cleanup
22
+ end
23
+
24
+ def stop
25
+ ActiveRecord::Base.remove_connection
26
+ end
27
+
28
+ private
29
+
30
+ def create_call_hook_for_connection_cleanup
31
+ Events.register_callback([:asterisk, :before_call]) do
32
+ ActiveRecord::Base.verify_active_connections!
33
+ end
34
+ end
35
+
36
+ def require_dependencies
37
+ begin
38
+ require 'active_record'
39
+ rescue LoadError
40
+ ahn_log.fatal "Database support requires the \"activerecord\" gem."
41
+ # Silence the abort so we don't get an ugly backtrace
42
+ abort ""
43
+ end
44
+ end
45
+
46
+ def require_models
47
+ AHN_CONFIG.files_from_setting("paths", "models").each do |model|
48
+ load model
49
+ end
50
+ end
51
+
52
+ def establish_connection
53
+ ActiveRecord::Base.establish_connection @@config.connection_options
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ end
60
+ end