daemon-kit 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +17 -0
- data/Manifest.txt +62 -0
- data/PostInstall.txt +6 -0
- data/README.textile +94 -0
- data/Rakefile +31 -0
- data/TODO.txt +24 -0
- data/app_generators/daemon_kit/USAGE +7 -0
- data/app_generators/daemon_kit/daemon_kit_generator.rb +120 -0
- data/app_generators/daemon_kit/templates/README +48 -0
- data/app_generators/daemon_kit/templates/Rakefile +4 -0
- data/app_generators/daemon_kit/templates/bin/daemon.erb +7 -0
- data/app_generators/daemon_kit/templates/config/boot.rb +68 -0
- data/app_generators/daemon_kit/templates/config/environment.rb +19 -0
- data/app_generators/daemon_kit/templates/config/environments/development.rb +0 -0
- data/app_generators/daemon_kit/templates/config/environments/production.rb +0 -0
- data/app_generators/daemon_kit/templates/config/environments/test.rb +0 -0
- data/app_generators/daemon_kit/templates/config/initializers/readme +11 -0
- data/app_generators/daemon_kit/templates/libexec/daemon.erb +18 -0
- data/bin/daemon_kit +19 -0
- data/daemon_generators/amqp/USAGE +5 -0
- data/daemon_generators/amqp/amqp_generator.rb +65 -0
- data/daemon_generators/amqp/templates/config/amqp.yml +28 -0
- data/daemon_generators/amqp/templates/config/initializers/amqp.rb +7 -0
- data/daemon_generators/amqp/templates/libexec/daemon.rb +29 -0
- data/daemon_generators/cron/USAGE +5 -0
- data/daemon_generators/cron/cron_generator.rb +64 -0
- data/daemon_generators/cron/templates/config/initializers/cron.rb +7 -0
- data/daemon_generators/cron/templates/libexec/daemon.rb +39 -0
- data/daemon_generators/jabber/USAGE +5 -0
- data/daemon_generators/jabber/jabber_generator.rb +65 -0
- data/daemon_generators/jabber/templates/config/initializers/jabber.rb +7 -0
- data/daemon_generators/jabber/templates/config/jabber.yml +26 -0
- data/daemon_generators/jabber/templates/libexec/daemon.rb +27 -0
- data/lib/daemon_kit.rb +14 -0
- data/lib/daemon_kit/amqp.rb +41 -0
- data/lib/daemon_kit/application.rb +34 -0
- data/lib/daemon_kit/cron.rb +38 -0
- data/lib/daemon_kit/initializer.rb +255 -0
- data/lib/daemon_kit/jabber.rb +172 -0
- data/lib/daemon_kit/patches/force_kill_wait.rb +120 -0
- data/lib/daemon_kit/tasks.rb +2 -0
- data/lib/daemon_kit/tasks/framework.rake +75 -0
- data/rubygems_generators/install_rspec/USAGE +5 -0
- data/rubygems_generators/install_rspec/install_rspec_generator.rb +57 -0
- data/rubygems_generators/install_rspec/templates/spec.rb +11 -0
- data/rubygems_generators/install_rspec/templates/spec/spec.opts +1 -0
- data/rubygems_generators/install_rspec/templates/spec/spec_helper.rb +10 -0
- data/rubygems_generators/install_rspec/templates/tasks/rspec.rake +21 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +71 -0
- data/spec/daemon_kit_spec.rb +7 -0
- data/spec/initializer_spec.rb +31 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +30 -0
- data/tasks/rspec.rake +21 -0
- data/test/test_amqp_generator.rb +48 -0
- data/test/test_cron_generator.rb +45 -0
- data/test/test_daemon-kit_generator.rb +67 -0
- data/test/test_generator_helper.rb +29 -0
- data/test/test_jabber_generator.rb +49 -0
- metadata +168 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# Generated jabber daemon
|
2
|
+
|
3
|
+
# Do your post daemonization configuration here
|
4
|
+
# At minimum you need just the first line (without the block), or a lot
|
5
|
+
# of strange things might start happening...
|
6
|
+
DaemonKit::Application.running! do |config|
|
7
|
+
# Trap signals with blocks or procs
|
8
|
+
# config.trap( 'INT' ) do
|
9
|
+
# # do something clever
|
10
|
+
# end
|
11
|
+
# config.trap( 'TERM', Proc.new { puts 'Going down' } )
|
12
|
+
end
|
13
|
+
|
14
|
+
# IMPORTANT CONFIGURATION NOTE
|
15
|
+
#
|
16
|
+
# Please review and update 'config/jabber.yml' accordingly or this
|
17
|
+
# daemon won't work as advertised.
|
18
|
+
|
19
|
+
# This block gets called every time a message has been received from a
|
20
|
+
# valid master.
|
21
|
+
DaemonKit::Jabber.received_messages do |message|
|
22
|
+
# Simple echo service
|
23
|
+
DaemonKit::Jabber.deliver( message.from, message.body )
|
24
|
+
end
|
25
|
+
|
26
|
+
# Run our Jabber bot
|
27
|
+
DaemonKit::Jabber.run
|
data/lib/daemon_kit.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
module DaemonKit
|
7
|
+
VERSION = '0.1.3'
|
8
|
+
|
9
|
+
autoload :Initializer, 'daemon_kit/initializer'
|
10
|
+
autoload :Application, 'daemon_kit/application'
|
11
|
+
autoload :Cron, 'daemon_kit/cron'
|
12
|
+
autoload :Jabber, 'daemon_kit/jabber'
|
13
|
+
autoload :AMQP, 'daemon_kit/amqp'
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
# Thin wrapper around the amqp gem, specifically designed to ease
|
5
|
+
# configuration of a AMQP consumer daemon and provide some added
|
6
|
+
# simplicity
|
7
|
+
class AMQP
|
8
|
+
|
9
|
+
@@instance = nil
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def instance
|
14
|
+
@instance ||= (
|
15
|
+
config = YAML.load_file( "#{DAEMON_ROOT}/config/amqp.yml" )[DAEMON_ENV]
|
16
|
+
raise ArgumentError, "Missing AMQP configuration for #{DAEMON_ENV} environment" if config.nil?
|
17
|
+
new( config )
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private :new
|
22
|
+
|
23
|
+
def run(&block)
|
24
|
+
instance.run(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize( config = {} )
|
29
|
+
@config = config.inject({}) { |m,c| m[c[0].to_sym] = c[1]; m } # symbolize_keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(&block)
|
33
|
+
# Ensure graceful shutdown of the connection to the broker
|
34
|
+
DaemonKit.trap('INT') { ::AMQP.stop { ::EM.stop } }
|
35
|
+
DaemonKit.trap('TERM') { ::AMQP.stop { ::EM.stop } }
|
36
|
+
|
37
|
+
# Start our event loop
|
38
|
+
::AMQP.start(@config, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'daemons'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
|
5
|
+
# Class responsible for making the daemons run and keep them running.
|
6
|
+
class Application
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Run the file as a daemon
|
11
|
+
def run( file )
|
12
|
+
raise DaemonNotFound.new( file ) unless File.exist?( file )
|
13
|
+
|
14
|
+
app_name = DaemonKit.configuration.daemon_name || File.basename( file )
|
15
|
+
options = { :backtrace => true, :log_output => true, :app_name => app_name }
|
16
|
+
|
17
|
+
options[:dir_mode] = DaemonKit.configuration.dir_mode || :normal
|
18
|
+
options[:dir] = DaemonKit.configuration.dir || "log"
|
19
|
+
options[:multiple] = DaemonKit.configuration.multiple
|
20
|
+
options[:force_kill_wait] = DaemonKit.configuration.force_kill_wait if DaemonKit.configuration.force_kill_wait
|
21
|
+
|
22
|
+
Daemons.run( file, options )
|
23
|
+
end
|
24
|
+
|
25
|
+
# Call this from inside a daemonized process to complete the
|
26
|
+
# initialization process
|
27
|
+
def running!
|
28
|
+
DaemonKit::Initializer.continue!
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module DaemonKit
|
2
|
+
|
3
|
+
# Thin wrapper around rufus-scheduler gem, specifically designed to ease
|
4
|
+
# configuration of a scheduler and provide some added simplicity.
|
5
|
+
class Cron
|
6
|
+
|
7
|
+
@@instance = nil
|
8
|
+
|
9
|
+
attr_reader :scheduler
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def instance
|
14
|
+
@instance ||= new
|
15
|
+
end
|
16
|
+
|
17
|
+
def scheduler
|
18
|
+
instance.scheduler
|
19
|
+
end
|
20
|
+
|
21
|
+
private :new
|
22
|
+
|
23
|
+
def run
|
24
|
+
DaemonKit.logger.info "Starting rufus-scheduler"
|
25
|
+
|
26
|
+
begin
|
27
|
+
instance.scheduler.join
|
28
|
+
rescue Interrupt
|
29
|
+
DaemonKit.logger.warn "Scheduler interrupted"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@scheduler = Rufus::Scheduler.start_new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
DAEMON_ENV = (ENV['DAEMON_ENV'] || 'development').dup unless defined?(DAEMON_ENV)
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__) + '/..'
|
7
|
+
require 'daemon_kit'
|
8
|
+
|
9
|
+
module DaemonKit
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def logger
|
14
|
+
@logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger=( logger )
|
18
|
+
@logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def configuration
|
22
|
+
@configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
def configuration=( configuration )
|
26
|
+
@configuration = configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def trap( *args, &block )
|
30
|
+
self.configuration.trap( *args, &block )
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# This class does all the nightmare work of setting up a working
|
37
|
+
# environment for your daemon.
|
38
|
+
class Initializer
|
39
|
+
|
40
|
+
attr_reader :configuration
|
41
|
+
|
42
|
+
def self.run( configuration = Configuration.new )
|
43
|
+
yield configuration if block_given?
|
44
|
+
initializer = new configuration
|
45
|
+
initializer.before_daemonize
|
46
|
+
initializer
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.continue!
|
50
|
+
initializer = new DaemonKit.configuration
|
51
|
+
initializer.after_daemonize
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.shutdown
|
55
|
+
DaemonKit.logger.warn "Shutting down"
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize( configuration )
|
60
|
+
@configuration = configuration
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_daemonize
|
64
|
+
DaemonKit.configuration = @configuration
|
65
|
+
|
66
|
+
set_load_path
|
67
|
+
load_gems
|
68
|
+
load_patches
|
69
|
+
load_environment
|
70
|
+
end
|
71
|
+
|
72
|
+
def after_daemonize
|
73
|
+
initialize_logger
|
74
|
+
initialize_signal_traps
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_load_path
|
78
|
+
configuration.load_paths.each do |d|
|
79
|
+
$:.unshift( "#{DAEMON_ROOT}/#{d}" ) if File.directory?( "#{DAEMON_ROOT}/#{d}" )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_gems
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_patches
|
88
|
+
if !!configuration.force_kill_wait
|
89
|
+
require 'daemon_kit/patches/force_kill_wait'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def load_environment
|
94
|
+
return if @environment_loaded
|
95
|
+
@environment_loaded = true
|
96
|
+
|
97
|
+
config = configuration
|
98
|
+
|
99
|
+
eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
|
100
|
+
|
101
|
+
eval(IO.read(configuration.daemon_initializer), binding, configuration.daemon_initializer) if File.exist?( configuration.daemon_initializer )
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize_logger
|
105
|
+
return if DaemonKit.logger
|
106
|
+
|
107
|
+
unless logger = configuration.logger
|
108
|
+
logger = Logger.new( configuration.log_path )
|
109
|
+
logger.level = configuration.log_level
|
110
|
+
end
|
111
|
+
|
112
|
+
DaemonKit.logger = logger
|
113
|
+
|
114
|
+
configuration.trap("USR1") {
|
115
|
+
DaemonKit.logger.level = DaemonKit.logger.debug? ? Logger::INFO : Logger::DEBUG
|
116
|
+
DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
|
117
|
+
}
|
118
|
+
configuration.trap("USR2") {
|
119
|
+
DaemonKit.logger.level = Logger::DEBUG
|
120
|
+
DaemonKit.logger.info "Log level changed to DEBUG"
|
121
|
+
}
|
122
|
+
|
123
|
+
DaemonKit.logger.info "DaemonKit up and running in #{DAEMON_ENV} mode"
|
124
|
+
end
|
125
|
+
|
126
|
+
def initialize_signal_traps
|
127
|
+
term_proc = Proc.new { DaemonKit::Initializer.shutdown }
|
128
|
+
configuration.trap( 'INT', term_proc )
|
129
|
+
configuration.trap( 'TERM', term_proc )
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
# Holds our various configuration values
|
135
|
+
class Configuration
|
136
|
+
# Root to the daemon
|
137
|
+
attr_reader :root_path
|
138
|
+
|
139
|
+
# List of load paths
|
140
|
+
attr_accessor :load_paths
|
141
|
+
|
142
|
+
# The log level to use, defaults to DEBUG
|
143
|
+
attr_accessor :log_level
|
144
|
+
|
145
|
+
# Path to the log file, defaults to 'log/<environment>.log'
|
146
|
+
attr_accessor :log_path
|
147
|
+
|
148
|
+
# :system,
|
149
|
+
attr_accessor :dir_mode
|
150
|
+
|
151
|
+
# Path to the log file, defaults to 'log/<environment>.log'
|
152
|
+
attr_accessor :dir
|
153
|
+
|
154
|
+
# Provide a custom logger to use
|
155
|
+
attr_accessor :logger
|
156
|
+
|
157
|
+
# The application name
|
158
|
+
attr_accessor :daemon_name
|
159
|
+
|
160
|
+
# Allow multiple copies to run?
|
161
|
+
attr_accessor :multiple
|
162
|
+
|
163
|
+
# Use the force kill patch? Give the number of seconds
|
164
|
+
attr_accessor :force_kill_wait
|
165
|
+
|
166
|
+
# Collection of signal traps
|
167
|
+
attr_reader :signal_traps
|
168
|
+
|
169
|
+
def initialize
|
170
|
+
set_root_path!
|
171
|
+
|
172
|
+
self.load_paths = default_load_paths
|
173
|
+
self.log_level = default_log_level
|
174
|
+
self.log_path = default_log_path
|
175
|
+
|
176
|
+
self.multiple = false
|
177
|
+
self.force_kill_wait = false
|
178
|
+
|
179
|
+
@signal_traps = {}
|
180
|
+
end
|
181
|
+
|
182
|
+
def environment
|
183
|
+
::DAEMON_ENV
|
184
|
+
end
|
185
|
+
|
186
|
+
# The path to the current environment's file (<tt>development.rb</tt>, etc.). By
|
187
|
+
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
|
188
|
+
def environment_path
|
189
|
+
"#{root_path}/config/environments/#{environment}.rb"
|
190
|
+
end
|
191
|
+
|
192
|
+
def daemon_initializer
|
193
|
+
"#{root_path}/config/initializers/#{self.daemon_name}.rb"
|
194
|
+
end
|
195
|
+
|
196
|
+
# Add a trap for the specified signal, can be code block or a proc
|
197
|
+
def trap( signal, proc = nil, &block )
|
198
|
+
return if proc.nil? && !block_given?
|
199
|
+
|
200
|
+
unless @signal_traps.has_key?( signal )
|
201
|
+
set_trap( signal )
|
202
|
+
end
|
203
|
+
|
204
|
+
@signal_traps[signal].unshift( proc || block )
|
205
|
+
end
|
206
|
+
|
207
|
+
protected
|
208
|
+
|
209
|
+
def run_traps( signal )
|
210
|
+
DaemonKit.logger.info "Running signal traps for #{signal}"
|
211
|
+
self.signal_traps[ signal ].each { |trap| trap.call }
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def set_trap( signal )
|
217
|
+
DaemonKit.logger.info "Setting up trap for #{signal}"
|
218
|
+
@signal_traps[ signal ] = []
|
219
|
+
Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
|
220
|
+
end
|
221
|
+
|
222
|
+
def set_root_path!
|
223
|
+
raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
|
224
|
+
raise "DAEMON_ROOT is not a directory" unless defined?(::DAEMON_ROOT)
|
225
|
+
|
226
|
+
@root_path =
|
227
|
+
# Pathname is incompatible with Windows, but Windows doesn't have
|
228
|
+
# real symlinks so File.expand_path is safe.
|
229
|
+
if RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
230
|
+
File.expand_path(::DAEMON_ROOT)
|
231
|
+
|
232
|
+
# Otherwise use Pathname#realpath which respects symlinks.
|
233
|
+
else
|
234
|
+
Pathname.new(::DAEMON_ROOT).realpath.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
|
238
|
+
::DAEMON_ROOT.replace @root_path
|
239
|
+
end
|
240
|
+
|
241
|
+
def default_load_paths
|
242
|
+
[ 'lib' ]
|
243
|
+
end
|
244
|
+
|
245
|
+
def default_log_path
|
246
|
+
File.join(root_path, 'log', "#{environment}.log")
|
247
|
+
end
|
248
|
+
|
249
|
+
def default_log_level
|
250
|
+
environment == 'production' ? Logger::INFO : Logger::DEBUG
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
# Thin wrapper around xmpp4r-simple, specifically designed to ease
|
5
|
+
# configuration of a jabber daemon and provide some added simplicity.
|
6
|
+
class Jabber
|
7
|
+
|
8
|
+
# Jabber connection
|
9
|
+
attr_reader :connection
|
10
|
+
|
11
|
+
@@instance = nil
|
12
|
+
@@message_handler = nil
|
13
|
+
@@presence_handler = nil
|
14
|
+
@@subscription_handler = nil
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Deliver a message to the specified jid.
|
19
|
+
def deliver( jid, message )
|
20
|
+
instance.connection.deliver( jid, message )
|
21
|
+
end
|
22
|
+
|
23
|
+
# Use this instead of initializing, keeps it singleton
|
24
|
+
def instance
|
25
|
+
@instance ||= (
|
26
|
+
config = YAML.load_file( "#{DAEMON_ROOT}/config/jabber.yml" )[DAEMON_ENV]
|
27
|
+
raise ArgumentError, "Missing Jabber configuration for #{DAEMON_ENV} environment" if config.nil?
|
28
|
+
new( config )
|
29
|
+
)
|
30
|
+
@instance.startup!
|
31
|
+
end
|
32
|
+
private :new
|
33
|
+
|
34
|
+
def run
|
35
|
+
DaemonKit.logger.info "Starting jabber loop"
|
36
|
+
|
37
|
+
loop do
|
38
|
+
process_messages
|
39
|
+
process_updates
|
40
|
+
process_subscriptions
|
41
|
+
|
42
|
+
begin
|
43
|
+
sleep 1
|
44
|
+
rescue Interrupt
|
45
|
+
DaemonKit.logger.warn "Jabber loop interrupted"
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_messages
|
52
|
+
@message_handler ||= Proc.new { |m| DaemonKit.logger.info "Received message from #{m.from}: #{m.body}" }
|
53
|
+
|
54
|
+
instance.valid_messages { |m| @message_handler.call(m) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_updates
|
58
|
+
@presence_handler ||= Proc.new { |friend, old_presence, new_presence|
|
59
|
+
DaemonKit.logger.debug "Received presence update: #{friend} went from #{old_presence} to #{new_presence}"
|
60
|
+
}
|
61
|
+
|
62
|
+
instance.connection.presence_updates { |friend, old_presence, new_presence|
|
63
|
+
@presence_handler.call(friend, old_presence, new_presence)
|
64
|
+
}
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_subscriptions
|
69
|
+
@subscription_handler ||= Proc.new { |friend,presence| DaemonKit.logger.debug "Received presence update from #{friend}: #{presence}" }
|
70
|
+
|
71
|
+
instance.connection.subscription_requests { |friend,presence| @subscription_handler.call(friend,presence) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def received_messages(&block)
|
75
|
+
@message_handler = block
|
76
|
+
end
|
77
|
+
|
78
|
+
def presence_updates(&block)
|
79
|
+
@presence_handler = block
|
80
|
+
end
|
81
|
+
|
82
|
+
def subscription_requests(&block)
|
83
|
+
@subscription_handler = block
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize( options = {} )
|
89
|
+
@jabber_id = options.delete("jabber_id")
|
90
|
+
@password = options.delete("password")
|
91
|
+
@resource = options.delete("resource") || 'daemon_kit'
|
92
|
+
@masters = options.delete("masters") || []
|
93
|
+
@supporters = options.delete("supporters") || []
|
94
|
+
|
95
|
+
raise ArgumentError if [ @jabber_id, @password ].any? { |a| a.nil? }
|
96
|
+
end
|
97
|
+
|
98
|
+
def startup!
|
99
|
+
return self if @booted
|
100
|
+
|
101
|
+
connect!
|
102
|
+
setup_roster!
|
103
|
+
|
104
|
+
DaemonKit.trap( 'INT', Proc.new { self.shutdown! } )
|
105
|
+
DaemonKit.trap( 'TERM', Proc.new { self.shutdown! } )
|
106
|
+
|
107
|
+
@booted = true
|
108
|
+
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def shutdown!
|
113
|
+
DaemonKit.logger.warn "Disconnecting jabber connection"
|
114
|
+
self.connection.disconnect
|
115
|
+
end
|
116
|
+
|
117
|
+
def contacts
|
118
|
+
@masters + @supporters
|
119
|
+
end
|
120
|
+
|
121
|
+
def valid_messages(&block)
|
122
|
+
self.connection.received_messages.each do |message|
|
123
|
+
next unless valid_master?( message.from )
|
124
|
+
|
125
|
+
busy do
|
126
|
+
block.call message
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def valid_master?( jid )
|
132
|
+
@masters.include?( jid.strip.to_s )
|
133
|
+
end
|
134
|
+
|
135
|
+
def busy(&block)
|
136
|
+
self.connection.status(:dnd, "Working...")
|
137
|
+
yield
|
138
|
+
self.connection.status(:chat, self.status_line )
|
139
|
+
end
|
140
|
+
|
141
|
+
def status_line
|
142
|
+
"#{DaemonKit.configuration.daemon_name} ready for instructions"
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def connect!
|
148
|
+
jid = @jabber_id + '/' + @resource
|
149
|
+
|
150
|
+
@connection = ::Jabber::Simple.new( jid, @password, nil, self.status_line )
|
151
|
+
end
|
152
|
+
|
153
|
+
def setup_roster!
|
154
|
+
# cleanup the roster
|
155
|
+
self.connection.roster.items.each_pair do |jid, roster_item|
|
156
|
+
jid = jid.strip.to_s
|
157
|
+
unless self.contacts.include?( jid )
|
158
|
+
self.connection.remove( jid )
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# add missing contacts
|
163
|
+
self.contacts.each do |jid|
|
164
|
+
unless self.connection.subscribed_to?( jid )
|
165
|
+
self.connection.add( jid )
|
166
|
+
#self.connection.accept_subscription( jid )
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|