daemon-kit 0.1.3
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/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
|