updater 0.9.2 → 0.9.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/VERSION +1 -1
- data/bin/updater +101 -8
- data/lib/updater/orm/datamapper.rb +1 -1
- data/lib/updater/orm/mongo.rb +276 -0
- data/lib/updater/orm/mongo.rb~ +188 -0
- data/lib/updater/orm/orm.rb +1 -1
- data/lib/updater/setup.rb +56 -22
- data/lib/updater/setup.rb~ +175 -0
- data/lib/updater/thread_worker.rb +1 -2
- data/lib/updater/thread_worker.rb~ +92 -0
- data/lib/updater/update.rb +19 -8
- data/spec/spec_helper.rb +3 -2
- data/spec/spec_helper.rb~ +23 -0
- metadata +15 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.3
|
data/bin/updater
CHANGED
@@ -1,18 +1,111 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
require 'optparse'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
require 'updater'
|
8
|
-
equire 'updater/worker'
|
5
|
+
OPERATIONS = %w{start stop noop} # restart}
|
6
|
+
WORKERS = %w{fork thread}
|
9
7
|
|
8
|
+
options = {}
|
10
9
|
|
11
|
-
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.banner = <<EOF
|
12
|
+
Updater: Ruby Job Processor. Copyright John F. Miller 2009-2010
|
12
13
|
|
13
|
-
|
14
|
+
Usage: updater (#{OPERATIONS.join('|')}) [options]"
|
15
|
+
Start of stop the Updater job queue processor.
|
16
|
+
EOF
|
17
|
+
opts.on("-l","--local", "Use local 'lib' directory instead of Gems.") do
|
18
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-c","--config-file [FILE]", "File containing configuration data.", " default: ENV['UPDATE_CONFIG'] || updater.config") do |f|
|
22
|
+
options[:config_file] = f
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("--log-file [FILE]", "Where to send the log output.", " default: STDOUT") do |f|
|
26
|
+
options[:log_file] = f
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-d","--debug", "Output lots of extra data to the log file") do
|
30
|
+
options[:log_level] = "DEBUG"
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-w","--worker WORKERTYPE", "(#{WORKERS.join('|')}) type of worker to use", " default: fork (must be set to 'thread' for Windows)") do |w|
|
34
|
+
unless WORKERS.include? w
|
35
|
+
puts "** Invalid worker type **\n\n",opts
|
36
|
+
exit -1
|
37
|
+
end
|
38
|
+
options[:worker] = w
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-m","--monitor", "Start/Stop the HTTP queue monitor", " Not yet implimented") do #TODO
|
42
|
+
puts "HTTP Monitor Not Yet Implimented."
|
43
|
+
exit -1
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.separator "\n -- IPC Options: These options will need to be matched by the client --\n"
|
47
|
+
opts.on('-p','--pid-file FILE', 'The name of the PID file.') do |f|
|
48
|
+
options[:pid_file] = f
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-s','--unix-socket FILE', 'Socket to be used for UNIX Socket IPC.') do |f|
|
52
|
+
options[:socket] = f
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('--host [HOST]', 'For UDC and TCP the host name. (See Security section in README)', ' default: localhost') do |h|
|
56
|
+
options[:host] = 'localhost'
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-u','--udp PORT', 'Port to send/recieve UDP messages. (See Security section in README)') do |p|
|
60
|
+
options[:udp] = p
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-t','--tcp PORT', 'Port to send/recieve tcp messages. (See Security section in README)') do |p|
|
64
|
+
options[:tcp] = p
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-r','--remote-http PORT', 'Port to send/recieve HTTP service requests.', ' Not Yet Implimented') do |p|
|
68
|
+
options[:tcp] = p
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
opts.separator "\n -- Additional Information --\n"
|
73
|
+
opts.on_tail("-v", "--version", "Show Version information.") do
|
74
|
+
options[:version] = true
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on_tail("-h", "--help", "show this message.") do
|
78
|
+
puts opts
|
79
|
+
exit
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.parse!
|
84
|
+
|
85
|
+
require 'updater' #must wait for -l option
|
86
|
+
|
87
|
+
if options[:version]
|
88
|
+
puts(<<EOF)
|
89
|
+
Updater: Ruby Job Processor. Version #{Updater::VERSION}
|
90
|
+
Copyright John F. Miller 2009-2010
|
91
|
+
EOF
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
|
95
|
+
operation = ARGV.shift
|
96
|
+
|
97
|
+
operation = operation.downcase if operation
|
98
|
+
|
99
|
+
unless OPERATIONS.include? operation
|
100
|
+
puts "#{operation}: operation not supported" if operation
|
101
|
+
puts opts
|
102
|
+
exit
|
103
|
+
end
|
104
|
+
|
105
|
+
require 'updater/setup'
|
106
|
+
|
107
|
+
Updater::Setup.send(operation,options)
|
14
108
|
|
15
|
-
Worker.new.start
|
16
109
|
|
17
110
|
|
18
111
|
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
require 'active_support/inflector' #to get classes from strings
|
3
|
+
require 'active_support/core_ext/object/try'
|
4
|
+
|
5
|
+
module Updater
|
6
|
+
module ORM
|
7
|
+
class Mongo
|
8
|
+
|
9
|
+
FINDER= :get
|
10
|
+
ID=:_id
|
11
|
+
|
12
|
+
def initialize(hash = {})
|
13
|
+
@hash = {}
|
14
|
+
hash.each do |key, val|
|
15
|
+
if respond_to? "#{key}="
|
16
|
+
send("#{key}=", val)
|
17
|
+
else
|
18
|
+
@hash[key] = val
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
%w{time finder finder_args method method_args name persistant lock_name}.each do |field|
|
24
|
+
eval(<<-EOF) #,__LINE__+1,__FILE__)
|
25
|
+
def #{field};@hash['#{field}'];end
|
26
|
+
def #{field}=(value);@hash['#{field}'] = value;end
|
27
|
+
EOF
|
28
|
+
end
|
29
|
+
|
30
|
+
def _id
|
31
|
+
@hash['_id'] || @hash[:_id]
|
32
|
+
end
|
33
|
+
|
34
|
+
def _id=(val)
|
35
|
+
val = BSON::ObjectID.from_string(val.to_s) unless val.kind_of? BSON::ObjectID
|
36
|
+
@hash[:_id] = val
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :id :_id
|
40
|
+
alias :'id=' :'_id='
|
41
|
+
|
42
|
+
def target
|
43
|
+
@hash['target'].try :constantize
|
44
|
+
end
|
45
|
+
|
46
|
+
def target=(value)
|
47
|
+
@hash['target'] = value.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
def save
|
51
|
+
#todo validation
|
52
|
+
[:failure,:success,:ensure].each do |mode|
|
53
|
+
next unless @hash[mode]
|
54
|
+
@hash[mode] = @hash[mode].map do |job|
|
55
|
+
if job.kind_of? Updater::Update
|
56
|
+
job.save unless job.id
|
57
|
+
job = job.id
|
58
|
+
end
|
59
|
+
job
|
60
|
+
end
|
61
|
+
end
|
62
|
+
_id = self.class.collection.save @hash
|
63
|
+
end
|
64
|
+
|
65
|
+
def destroy
|
66
|
+
self.class.collection.remove({:_id=>id})
|
67
|
+
end
|
68
|
+
|
69
|
+
def [](arg) #this allows easy mapping for time when a value coud either be U::ORM::Mongo or an ordered hash
|
70
|
+
@hash[arg]
|
71
|
+
end
|
72
|
+
|
73
|
+
def lock(worker)
|
74
|
+
raise NotImplimentedError, "Use lock_next"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Non API Standard. This method returns the collection used by this instance.
|
78
|
+
# This is used to create Job Chains
|
79
|
+
def collection
|
80
|
+
self.class.instance_variable_get(:@collection)
|
81
|
+
end
|
82
|
+
|
83
|
+
#key :time, Integer, :numeric=>true
|
84
|
+
#key :target, String, :required => true
|
85
|
+
#key :finder, String
|
86
|
+
# key :finder_args, Array
|
87
|
+
#key :method, String :required => true
|
88
|
+
#key :method_args, String :required => true
|
89
|
+
#key :name
|
90
|
+
# key :persistant
|
91
|
+
#key :lock_name
|
92
|
+
|
93
|
+
%w{failure success ensure}.each do |mode|
|
94
|
+
eval(<<-EOF, binding ,__FILE__, __LINE__+1)
|
95
|
+
def #{mode}
|
96
|
+
@#{mode} ||= init_chain(:#{mode})
|
97
|
+
end
|
98
|
+
|
99
|
+
def #{mode}=(chain)
|
100
|
+
chain = [chain] unless chain.kind_of? Array
|
101
|
+
@#{mode} , @hash[:#{mode}] = build_chain_arrays(chain)
|
102
|
+
attach_intellegent_insertion(@#{mode},:#{mode},self) if @#{mode}
|
103
|
+
end
|
104
|
+
EOF
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
# this method is calld from he chain asignment methods eg. failure=(chain)
|
109
|
+
# chain is an array which may contain BSON::ObjectID's or Updater::Update's or both
|
110
|
+
# For BSON::ObjectID's we cannot initialize them as this could leed to infinate loops.
|
111
|
+
# (an object pool would solve this problem, but feels like overkill)
|
112
|
+
# The final result must be a @hash containing all the BSON::ObjectID' (forign keys)
|
113
|
+
# and possibly @failure containting all instanciated UpdaterUpdates read to be called
|
114
|
+
# or @failure set to nil with the chain instanciated on first use.
|
115
|
+
def build_chain_arrays(arr, build = false)
|
116
|
+
build ||= arr.any? {|j| k,_ = j; Updater::Update === k || Hash === k}
|
117
|
+
output = arr.inject({:ids=>[],:instances=>[]}) do |accl,j|
|
118
|
+
inst, id = rationalize_instance(j)
|
119
|
+
if inst.nil? && build
|
120
|
+
real_id,params = id #id could be an array
|
121
|
+
inst = Updater::Update.new(self.class.new(collection.find_one(real_id)))
|
122
|
+
inst.params = params #set params to the second element of the id array. If id is scale then set to nil.
|
123
|
+
end
|
124
|
+
accl[:ids] << id || inst #id will be nil only if inst has not ben saved.
|
125
|
+
accl[:instances] << inst if inst
|
126
|
+
accl
|
127
|
+
end
|
128
|
+
if build
|
129
|
+
return [output[:instances],output[:ids]]
|
130
|
+
end
|
131
|
+
[nil,output[:ids]]
|
132
|
+
end
|
133
|
+
|
134
|
+
# This method takes something that may be a reference to an instance(BSON::ObjectID/String),
|
135
|
+
# an instance its self (Updater::Update), or a Hash
|
136
|
+
# and returns a touple of the Updater::Update,BSON::ObjectID.
|
137
|
+
# This method will bot instanciate object from BSON::ObjectID's
|
138
|
+
# nor will it save Hashes inorder to obtain an ID (it will creat a new Updater::Update from the hash).
|
139
|
+
# Instead it will return nil in the appropriate place.
|
140
|
+
def rationalize_instance(val)
|
141
|
+
val = BSON::ObjectID.fron_string(val) if val.kind_of? String
|
142
|
+
case val #aval is the actual runable object, hval is a BSON::ObjectID that we can put into the Database
|
143
|
+
when Updater::Update
|
144
|
+
val.params ? [val,[val.id,val.params]] : [val,val.id]
|
145
|
+
when Hash
|
146
|
+
[Updater::Update.new(val),val['_id']]
|
147
|
+
when BSON::ObjectID
|
148
|
+
[nil,val]
|
149
|
+
when Array
|
150
|
+
rationalize_instance(val[0]).tap do |ret|
|
151
|
+
ret[0].params = val[1] if ret[0]
|
152
|
+
ret[1] = [ret[1],val[1]]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def attach_intellegent_insertion(arr,mode,parent)
|
158
|
+
arr.define_singleton_method '<<' do |val|
|
159
|
+
inst, id = rationalize_instance(val)
|
160
|
+
inst = Updater::Update.new(self.class.new(parent.collection.find_one(id))) unless inst
|
161
|
+
parent.instance_variable_get(:@hash)[mode] ||= []
|
162
|
+
parent.instance_variable_get(:@hash)[mode] << id || inst
|
163
|
+
super inst
|
164
|
+
end
|
165
|
+
arr
|
166
|
+
end
|
167
|
+
|
168
|
+
def init_chain(mode)
|
169
|
+
ret, @hash[mode] = build_chain_arrays(@hash[mode] || [],true)
|
170
|
+
attach_intellegent_insertion(ret,mode,self)
|
171
|
+
end
|
172
|
+
|
173
|
+
class << self
|
174
|
+
attr_accessor :db, :collection, :logger
|
175
|
+
|
176
|
+
# Availible options:
|
177
|
+
# * :database - *required* either an established Mongo::DB database OR the name of the database
|
178
|
+
# * :collection - which collection to store jobs in. Default: "updater"
|
179
|
+
#
|
180
|
+
# If a connection to the database must be established (ie :database is not a Mongo::DB)
|
181
|
+
# these options may be used to establish that connection.
|
182
|
+
# * :host - the host to connect to. Default: "localhost"
|
183
|
+
# * :port - the port to connect to. Default: 27017
|
184
|
+
# * :username/:password - if these are present, they will be used to authenticate against the database
|
185
|
+
def setup(options)
|
186
|
+
logger ||= options[:logger]
|
187
|
+
raise ArgumentError, "Must spesify the name of a databas when setting up Mongo driver" unless options[:database]
|
188
|
+
if options[:database].kind_of? ::Mongo::DB
|
189
|
+
@db = options[:database]
|
190
|
+
else
|
191
|
+
logger.info "Attempting to connect to mongodb at #{[options[:host] || "localhost", options[:port] || 27017].join(':')} database: \"#{options[:database]}\""
|
192
|
+
@db = ::Mongo::Connection.new(options[:host] || "localhost", options[:port] || 27017).db(options[:database].to_s)
|
193
|
+
if options[:username] && options[:password]
|
194
|
+
success = db.authenticate(options[:username] , options[:password])
|
195
|
+
raise RunTimeError, "Could not Authenticate with MongoDb \"#{options[:database]}\" Please check the username and password."
|
196
|
+
end
|
197
|
+
end
|
198
|
+
collection_name = options[:collection] || 'updater'
|
199
|
+
unless db.collection_names.include? collection_name
|
200
|
+
logger.warn "Updater MongoDB Driver is creating a new collection, \"#{collection_name}\" in \"#{options[:database]}\""
|
201
|
+
end
|
202
|
+
@collection = db.collection(collection_name)
|
203
|
+
end
|
204
|
+
|
205
|
+
def before_fork
|
206
|
+
@db.connection.close
|
207
|
+
end
|
208
|
+
|
209
|
+
def after_fork
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
def lock_next(worker)
|
214
|
+
hash = Hash.new
|
215
|
+
hash['findandmodify'] =@collection.name
|
216
|
+
hash['query'] = {:time=>{'$lte'=>tnow},:lock_name=>nil}
|
217
|
+
hash['sort'] =[[:time,'ascending']] #oldest first
|
218
|
+
hash['update'] = {'$set'=>{:lock_name=>worker.name}}
|
219
|
+
hash['new'] = true
|
220
|
+
|
221
|
+
|
222
|
+
ret = @db.command hash, :check_response=>false
|
223
|
+
return nil unless ret['ok'] == 1
|
224
|
+
return new(ret['value'])
|
225
|
+
end
|
226
|
+
|
227
|
+
def get(id)
|
228
|
+
id = BSON::ObjectID.from_string(id) if id.kind_of? String
|
229
|
+
new(@collection.find_one(id))
|
230
|
+
end
|
231
|
+
|
232
|
+
def current
|
233
|
+
raise NotImplementedError, "Mongo does not support lazy evaluation"
|
234
|
+
end
|
235
|
+
|
236
|
+
def current_load
|
237
|
+
@collection.find(:time=>{'$lte'=>tnow}, :lock_name=>nil).count
|
238
|
+
end
|
239
|
+
|
240
|
+
def delayed
|
241
|
+
@collection.find(:time=>{'$gt'=>tnow}).count
|
242
|
+
end
|
243
|
+
|
244
|
+
def future(start, finish)
|
245
|
+
@collection.find(:time=>{'$gt'=>start+tnow,'$lt'=>finish+tnow}).count
|
246
|
+
end
|
247
|
+
|
248
|
+
def queue_time
|
249
|
+
nxt = @collection.find_one({:lock_name=>nil, :time=>{'$ne'=>nil}}, :sort=>[[:time, :asc]], :fields=>[:time])
|
250
|
+
return nil unless nxt
|
251
|
+
return 0 if nxt['time'] <= tnow
|
252
|
+
return nxt['time'] - tnow
|
253
|
+
end
|
254
|
+
|
255
|
+
def create(hash)
|
256
|
+
ret = new(hash)
|
257
|
+
ret.save and ret
|
258
|
+
end
|
259
|
+
|
260
|
+
def clear_all
|
261
|
+
@collection.remove
|
262
|
+
end
|
263
|
+
|
264
|
+
def clear_locks(worker)
|
265
|
+
@collection.update({:lock_name=>worker.name},{'$unset'=>{:lock_name=>1}},:multi=>true)
|
266
|
+
end
|
267
|
+
|
268
|
+
private
|
269
|
+
def tnow
|
270
|
+
Updater::Update.time.now.to_i
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
require 'active_support/inflector' #to get classes from strings
|
3
|
+
require 'active_support/core_ext/object/try'
|
4
|
+
|
5
|
+
module Updater
|
6
|
+
module ORM
|
7
|
+
class Mongo
|
8
|
+
|
9
|
+
FINDER= :get
|
10
|
+
ID=:_id
|
11
|
+
|
12
|
+
def initialize(hash = {})
|
13
|
+
@hash = {}
|
14
|
+
hash.each do |key, val|
|
15
|
+
if respond_to? "#{key}="
|
16
|
+
send("#{key}=", val)
|
17
|
+
else
|
18
|
+
@hash[key] = val
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
%w{time finder finder_args method method_args name persistance lock_name}.each do |field|
|
24
|
+
eval(<<-EOF) #,__LINE__+1,__FILE__)
|
25
|
+
def #{field};@hash['#{field}'];end
|
26
|
+
def #{field}=(value);@hash['#{field}'] = value;end
|
27
|
+
EOF
|
28
|
+
end
|
29
|
+
|
30
|
+
def _id
|
31
|
+
@hash['_id'] || @hash[:_id]
|
32
|
+
end
|
33
|
+
|
34
|
+
def _id=(val)
|
35
|
+
val = BSON::ObjectID.from_string(val.to_s) unless val.kind_of? BSON::ObjectID
|
36
|
+
@hash[:_id] = val
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :id :_id
|
40
|
+
alias :'id=' :'_id='
|
41
|
+
|
42
|
+
def target
|
43
|
+
@hash['target'].try :constantize
|
44
|
+
end
|
45
|
+
|
46
|
+
def target=(value)
|
47
|
+
@hash['target'] = value.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
def save
|
51
|
+
#todo validation
|
52
|
+
self.class.collection.save @hash
|
53
|
+
end
|
54
|
+
|
55
|
+
def destroy
|
56
|
+
@collection.remove({:_id=>id})
|
57
|
+
end
|
58
|
+
|
59
|
+
def [](arg) #this allows easy mapping for time when a value coud either be U::ORM::Mongo or an ordered hash
|
60
|
+
@hash[arg]
|
61
|
+
end
|
62
|
+
|
63
|
+
#key :time, Integer, :numeric=>true
|
64
|
+
#key :target, String, :required => true
|
65
|
+
#key :finder, String
|
66
|
+
# key :finder_args, Array
|
67
|
+
#key :method, String :required => true
|
68
|
+
#key :method_args, String :required => true
|
69
|
+
#key :name
|
70
|
+
# key :persistant
|
71
|
+
#key :lock_name
|
72
|
+
|
73
|
+
%w{failure success ensure}.each do |mode|
|
74
|
+
eval(<<-EOF) #,__FILE__,__LINE__+1)
|
75
|
+
def #{mode}
|
76
|
+
@#{mode} ||= init_chain(:#{mode})
|
77
|
+
end
|
78
|
+
EOF
|
79
|
+
end
|
80
|
+
|
81
|
+
def init_chain(mode)
|
82
|
+
ret = [@hash[mode.to_s] || []].flatten
|
83
|
+
unless ret.empty?
|
84
|
+
ret = @collection.find(:_id=>{'$in'=>ret}).map {|i| self.class.new(i)}
|
85
|
+
end
|
86
|
+
ret.define_singleton_method '<<' do |val|
|
87
|
+
val = BSON::ObjectID.fron_string(val) if val.kind_of? String
|
88
|
+
aval,hval = case val
|
89
|
+
when self.class
|
90
|
+
[val,val.id]
|
91
|
+
when Hash
|
92
|
+
[self.class.new(val),val['_id']]
|
93
|
+
when BSON::ObjectID
|
94
|
+
[@collection.find_one(val),val]
|
95
|
+
end
|
96
|
+
@hash[mode] ||= []
|
97
|
+
@hash[mode] << hval
|
98
|
+
super aval
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def lock(worker)
|
103
|
+
raise NotImplimentedError, "Use lock_next"
|
104
|
+
end
|
105
|
+
|
106
|
+
class << self
|
107
|
+
attr_accessor :db, :collection, :logger
|
108
|
+
|
109
|
+
# Availible options:
|
110
|
+
# * :database - the name of the database *required*
|
111
|
+
# * :host - the host to connect to. Default: "localhost"
|
112
|
+
# * :port - the port to connect to. Default: 27017
|
113
|
+
# * :collection - which collection to store jobs in. Default: "Updater"
|
114
|
+
# * :username/:password - if these are present, they will be used to authenticate against the database
|
115
|
+
def setup(options)
|
116
|
+
logger = options[:logger]
|
117
|
+
raise ArgumentError, "Must spesify the name of a databas when setting up Mongo driver" unless options[:database]
|
118
|
+
logger.info "Attempting to connect to mongodb at #{[options[:host] || "localhost", options[:port] || 27017}
|
119
|
+
@db = ::Mongo::Connection.new(options[:host] || "localhost", options[:port] || 27017).db(options[:database].to_s)
|
120
|
+
if options[:username] && options[:password]
|
121
|
+
success = db.authenticate(options[:username] , options[:password])
|
122
|
+
raise RunTimeError, "Could not Authenticate with MongoDb \"#{options[:database]}\" Please check the username and password."
|
123
|
+
end
|
124
|
+
collection_name = options[:collection] || 'updater'
|
125
|
+
unless db.collection_names.include collection_name
|
126
|
+
logger.warn "Updater MongoDB Driver is creating a new collection, \"#{collection_name}\" in \"#{options[:database]}\""
|
127
|
+
end
|
128
|
+
@collection = db.collection(collection_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
def lock_next(worker)
|
132
|
+
hash = OrderedHash.new
|
133
|
+
hash['findandmodify'] =@collection.name
|
134
|
+
hash['query'] = {:time=>{'$lte'=>tnow},:lock_name=>nil}
|
135
|
+
hash['sort'] =[[:time,'ascending']] #oldest first
|
136
|
+
hash['update'] = {'$set'=>{:lock_name=>worker.name}}
|
137
|
+
hash['new'] = true
|
138
|
+
|
139
|
+
ret = @db.command hash
|
140
|
+
return nil unless ret['ok'] == 1
|
141
|
+
return new(ret['value'])
|
142
|
+
end
|
143
|
+
|
144
|
+
def get(id)
|
145
|
+
id = BSON::ObjectID.from_string(id) if id.kind_of? String
|
146
|
+
new(@collection.find_one(id))
|
147
|
+
end
|
148
|
+
|
149
|
+
def current
|
150
|
+
raise NotImplementedError, "Mongo does not support lazy evaluation"
|
151
|
+
end
|
152
|
+
|
153
|
+
def current_load
|
154
|
+
@collection.find(:time=>{'$lte'=>tnow}).count
|
155
|
+
end
|
156
|
+
|
157
|
+
def delayed
|
158
|
+
@collection.find(:time=>{'$gt'=>tnow}).count
|
159
|
+
end
|
160
|
+
|
161
|
+
def future(start, finish)
|
162
|
+
@collection.find(:time=>{'$gt'=>start+tnow,'$lt'=>finish+tnow}).count
|
163
|
+
end
|
164
|
+
|
165
|
+
def queue_time
|
166
|
+
nxt = @collection.find_one({:time=>{'$gt'=>3,'$lt'=>4}, :lock_name=>'foobar'}, :sort=>[[:time, :asc]], :fields=>[:time])
|
167
|
+
return nil unless nxt
|
168
|
+
return 0 if nxt['time'] <= tnow
|
169
|
+
return nxt['time'] - tnow
|
170
|
+
end
|
171
|
+
|
172
|
+
def create(hash)
|
173
|
+
new(hash).save
|
174
|
+
end
|
175
|
+
|
176
|
+
def clear_all
|
177
|
+
@collection.remove
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
def tnow
|
182
|
+
Updater::Update.time.now.to_i
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/updater/orm/orm.rb
CHANGED
@@ -106,7 +106,7 @@ module Updater
|
|
106
106
|
class Base
|
107
107
|
|
108
108
|
# Every ORM should set this constant to a symbol that matches the most
|
109
|
-
# obvious method used to retrive a known instance. :get or:find are likely
|
109
|
+
# obvious method used to retrive a known instance. :get or :find are likely
|
110
110
|
# candidates.
|
111
111
|
FINDER= nil
|
112
112
|
|
data/lib/updater/setup.rb
CHANGED
@@ -6,24 +6,30 @@ require 'erb'
|
|
6
6
|
module Updater
|
7
7
|
class Setup
|
8
8
|
class << self
|
9
|
-
def start
|
10
|
-
new(config_file).start
|
9
|
+
def start(options={})
|
10
|
+
new(config_file(options), options).start
|
11
11
|
end
|
12
12
|
|
13
|
-
def stop
|
14
|
-
new(config_file).stop
|
13
|
+
def stop(options={})
|
14
|
+
new(config_file(options), options).stop
|
15
|
+
end
|
16
|
+
|
17
|
+
def noop(options={})
|
18
|
+
new(config_file(options), options).noop
|
15
19
|
end
|
16
20
|
|
17
21
|
def client_setup(options = {})
|
18
|
-
new(config_file, options).client_setup
|
22
|
+
new(config_file(options), options).client_setup
|
19
23
|
end
|
20
24
|
|
21
25
|
def monitor
|
22
26
|
|
23
27
|
end
|
24
28
|
|
25
|
-
def config_file
|
26
|
-
if
|
29
|
+
def config_file(options = {})
|
30
|
+
if options[:config_file] && File.exists?(options[:config_file])
|
31
|
+
options[:config_file]
|
32
|
+
elsif ENV['UPDATE_CONFIG'] && File.exists(ENV['UPDATE_CONFIG'])
|
27
33
|
ENV['UPDATE_CONFIG']
|
28
34
|
else
|
29
35
|
(Dir.glob('{config,.}/updater.config') + Dir.glob('.updater')).first
|
@@ -36,12 +42,14 @@ module Updater
|
|
36
42
|
#extended used for clients who wnat to override parameters
|
37
43
|
def initialize(file_or_hash, extended = {})
|
38
44
|
@options = file_or_hash.kind_of?(Hash) ? file_or_hash : load_file(file_or_hash)
|
39
|
-
@options.merge(extended)
|
45
|
+
@options.merge!(extended)
|
40
46
|
@options[:pid_file] ||= File.join(ROOT,'updater.pid')
|
41
47
|
@options[:host] ||= "localhost"
|
42
48
|
@logger = @options[:logger] || Logger.new(@options[:log_file] || STDOUT)
|
43
49
|
level = Logger::SEV_LABEL.index(@options[:log_level].upcase) if @options[:log_level]
|
44
|
-
@logger.level = level || Logger::WARN
|
50
|
+
@logger.level = level || Logger::WARN unless @options[:logger] #only set this if we were not handed a logger
|
51
|
+
@logger.debug "Debugging output enabled"
|
52
|
+
Update.logger = @logger
|
45
53
|
end
|
46
54
|
|
47
55
|
def start
|
@@ -54,13 +62,26 @@ module Updater
|
|
54
62
|
|
55
63
|
def stop
|
56
64
|
Process.kill("TERM",File.read(@options[:pid_file]).to_i)
|
65
|
+
sleep 1.0
|
66
|
+
end
|
67
|
+
|
68
|
+
def noop
|
69
|
+
@logger.warn "NOOP: will not start service"
|
70
|
+
set_orm
|
71
|
+
init_orm
|
72
|
+
load_models
|
73
|
+
client_setup
|
74
|
+
@logger.debug @options.inspect
|
75
|
+
exit
|
57
76
|
end
|
58
77
|
|
59
|
-
# The client is responcible for loading classes and making connections. We will simply setup the Updater spesifics
|
78
|
+
# The client is responcible for loading classes and making connections. We will simply setup the Updater spesifics.
|
60
79
|
def client_setup
|
80
|
+
@logger.info "Updater Client is being initialized..."
|
61
81
|
set_orm
|
62
82
|
|
63
83
|
if @options[:socket] && File.exists?(@options[:socket])
|
84
|
+
@logger.debug "Using UNIX Socket \"#{@options[:socket]}\""
|
64
85
|
Updater::Update.socket = UNIXSocket.new(@options[:socket])
|
65
86
|
elsif @options[:udp]
|
66
87
|
socket = UDPSocket.new()
|
@@ -86,13 +107,13 @@ module Updater
|
|
86
107
|
#don't setup twice. Client setup might call this as part of server setup in which case it is already done
|
87
108
|
return false if Updater::Update.orm
|
88
109
|
orm = @options[:orm] || "datamapper"
|
89
|
-
case orm.downcase
|
110
|
+
case orm.to_s.downcase
|
90
111
|
when "datamapper"
|
91
112
|
require 'updater/orm/datamapper'
|
92
113
|
Updater::Update.orm = ORM::DataMapper
|
93
114
|
when "mongodb"
|
94
|
-
require 'updater/orm/
|
95
|
-
Updater::Update.orm = ORM::
|
115
|
+
require 'updater/orm/mongo'
|
116
|
+
Updater::Update.orm = ORM::Mongo
|
96
117
|
when "activerecord"
|
97
118
|
require 'updater/orm/activerecord'
|
98
119
|
Updater::Update.orm = ORM::ActiveRecord
|
@@ -103,18 +124,31 @@ module Updater
|
|
103
124
|
@logger.info "Data store '#{orm}' selected"
|
104
125
|
end
|
105
126
|
|
106
|
-
def
|
107
|
-
#set ORM
|
108
|
-
set_orm
|
109
|
-
#init DataStore
|
127
|
+
def init_orm
|
110
128
|
default_options = {:adapter=>'sqlite3', :database=>'./default.db'}
|
111
129
|
Updater::Update.orm.setup((@options[:database] || @options[:orm_setup] || default_options).merge(:logger=>@logger))
|
112
|
-
|
113
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
def load_models
|
133
|
+
@logger.info "Loading Models..."
|
114
134
|
models = @options[:models] || Dir.glob('./app/models/**/*.rb')
|
115
135
|
models.each do |file|
|
116
|
-
|
136
|
+
@logger.debug " - loading file: #{file}"
|
137
|
+
begin
|
138
|
+
require file
|
139
|
+
rescue LoadError
|
140
|
+
require File.expand_path(File.join(Dir.pwd,file))
|
141
|
+
end
|
117
142
|
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def _start
|
146
|
+
#set ORM
|
147
|
+
set_orm
|
148
|
+
#init DataStore
|
149
|
+
init_orm
|
150
|
+
#load Models
|
151
|
+
load_models
|
118
152
|
|
119
153
|
#establish Connections
|
120
154
|
@options[:host] ||= 'localhost'
|
@@ -164,7 +198,7 @@ module Updater
|
|
164
198
|
@config_file = File.expand_path(file.path)
|
165
199
|
YAML.load(ERB.new(file.read).result(binding)) || {}
|
166
200
|
ensure
|
167
|
-
file.close
|
201
|
+
file.close if file.kind_of?(IO) && !file.closed?
|
168
202
|
end
|
169
203
|
end
|
170
|
-
end
|
204
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'yaml'
|
3
|
+
require 'socket'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module Updater
|
7
|
+
class Setup
|
8
|
+
class << self
|
9
|
+
def start
|
10
|
+
new(config_file).start
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop
|
14
|
+
new(config_file).stop
|
15
|
+
end
|
16
|
+
|
17
|
+
def client_setup(options = {})
|
18
|
+
new(config_file, options).client_setup
|
19
|
+
end
|
20
|
+
|
21
|
+
def monitor
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def config_file(options = {})
|
26
|
+
if options[:config_file] && File.exists(options[:config_file])
|
27
|
+
option[:config_file]
|
28
|
+
elsif ENV['UPDATE_CONFIG'] && File.exists(ENV['UPDATE_CONFIG'])
|
29
|
+
ENV['UPDATE_CONFIG']
|
30
|
+
else
|
31
|
+
(Dir.glob('{config,.}/updater.config') + Dir.glob('.updater')).first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
ROOT = File.dirname(self.config_file || Dir.pwd)
|
37
|
+
|
38
|
+
#extended used for clients who wnat to override parameters
|
39
|
+
def initialize(file_or_hash, extended = {})
|
40
|
+
@options = file_or_hash.kind_of?(Hash) ? file_or_hash : load_file(file_or_hash)
|
41
|
+
@options.merge!(extended)
|
42
|
+
@options[:pid_file] ||= File.join(ROOT,'updater.pid')
|
43
|
+
@options[:host] ||= "localhost"
|
44
|
+
@logger = @options[:logger] || Logger.new(@options[:log_file] || STDOUT)
|
45
|
+
level = Logger::SEV_LABEL.index(@options[:log_level].upcase) if @options[:log_level]
|
46
|
+
@logger.level = level || Logger::WARN unless @options[:logger] #only set this if we were not handed a logger
|
47
|
+
end
|
48
|
+
|
49
|
+
def start
|
50
|
+
pid = Process.fork do
|
51
|
+
_start
|
52
|
+
end
|
53
|
+
@logger.warn "Successfully started Master Loop at pid #{pid}"
|
54
|
+
puts "Job Queue Processor Started at PID: #{pid}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
Process.kill("TERM",File.read(@options[:pid_file]).to_i)
|
59
|
+
end
|
60
|
+
|
61
|
+
# The client is responcible for loading classes and making connections. We will simply setup the Updater spesifics
|
62
|
+
def client_setup
|
63
|
+
@logger.info "Updater Client is being initialized..."
|
64
|
+
set_orm
|
65
|
+
|
66
|
+
if @options[:socket] && File.exists?(@options[:socket])
|
67
|
+
@logger.debug "Using UNIX Socket \"#{@options[:socket]}\""
|
68
|
+
Updater::Update.socket = UNIXSocket.new(@options[:socket])
|
69
|
+
elsif @options[:udp]
|
70
|
+
socket = UDPSocket.new()
|
71
|
+
socket.connect(@options[:host],@options[:udp])
|
72
|
+
Updater::Update.socket = socket
|
73
|
+
elsif @options[:tcp]
|
74
|
+
Updater::Update.socket = TCPSocket.new(@options[:host],@options[:tcp])
|
75
|
+
elsif @options[:remote]
|
76
|
+
raise NotImplimentedError #For future Authenticated Http Rest Server
|
77
|
+
end
|
78
|
+
|
79
|
+
#set PID
|
80
|
+
if File.exists? @options[:pid_file]
|
81
|
+
Updater::Update.pid = File.read(@options[:pid_file]).strip
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def set_orm
|
90
|
+
#don't setup twice. Client setup might call this as part of server setup in which case it is already done
|
91
|
+
return false if Updater::Update.orm
|
92
|
+
orm = @options[:orm] || "datamapper"
|
93
|
+
@logger.info "Updater setting ORM to \"#{orm}\""
|
94
|
+
case orm.to_s.downcase
|
95
|
+
when "datamapper"
|
96
|
+
require 'updater/orm/datamapper'
|
97
|
+
Updater::Update.orm = ORM::DataMapper
|
98
|
+
when "mongodb"
|
99
|
+
require 'updater/orm/mongo'
|
100
|
+
Updater::Update.orm = ORM::Mongo
|
101
|
+
when "activerecord"
|
102
|
+
require 'updater/orm/activerecord'
|
103
|
+
Updater::Update.orm = ORM::ActiveRecord
|
104
|
+
else
|
105
|
+
require "update/orm/#{orm}"
|
106
|
+
Updater::Update.orm = Object.const_get("ORM").const_get(orm.capitalize)
|
107
|
+
end
|
108
|
+
@logger.info "Data store '#{orm}' selected"
|
109
|
+
end
|
110
|
+
|
111
|
+
def _start
|
112
|
+
#set ORM
|
113
|
+
set_orm
|
114
|
+
#init DataStore
|
115
|
+
default_options = {:adapter=>'sqlite3', :database=>'./default.db'}
|
116
|
+
Updater::Update.orm.setup((@options[:database] || @options[:orm_setup] || default_options).merge(:logger=>@logger))
|
117
|
+
#load Models
|
118
|
+
|
119
|
+
models = @options[:models] || Dir.glob('./app/models/**/*.rb')
|
120
|
+
models.each do |file|
|
121
|
+
require file
|
122
|
+
end
|
123
|
+
|
124
|
+
#establish Connections
|
125
|
+
@options[:host] ||= 'localhost'
|
126
|
+
#Unix Socket -- name at @options[:socket]
|
127
|
+
if @options[:socket]
|
128
|
+
File.unlink @options[:socket] if File.exists? @options[:socket]
|
129
|
+
@options[:sockets] ||= []
|
130
|
+
@options[:sockets] << UNIXServer.new(@options[:socket])
|
131
|
+
@logger.info "Now listening on UNIX Socket: #{@options[:socket]}"
|
132
|
+
end
|
133
|
+
|
134
|
+
#UDP potentially unsafe user monitor server for Authenticated Connections (TODO)
|
135
|
+
if @options[:udp]
|
136
|
+
@options[:sockets] ||= []
|
137
|
+
udp = UDPSocket.new
|
138
|
+
udp.bind(@options[:host],@options[:udp])
|
139
|
+
@options[:sockets] << udp
|
140
|
+
@logger.info "Now listening for UDP: #{@options[:host]}:#{@options[:udp]}"
|
141
|
+
end
|
142
|
+
|
143
|
+
#TCP Unsafe user monitor server for Authenticated Connections (TODO)
|
144
|
+
if @options[:tcp]
|
145
|
+
@options[:sockets] ||= []
|
146
|
+
@options[:sockets] << TCPServer.new(@options[:host],@options[:tcp])
|
147
|
+
@logger.info "Now listening for TCP: #{@options[:host]}:#{@options[:tcp]}"
|
148
|
+
end
|
149
|
+
|
150
|
+
#Log PID
|
151
|
+
File.open(@options[:pid_file],'w') { |f| f.write(Process.pid.to_s)}
|
152
|
+
|
153
|
+
client_setup
|
154
|
+
|
155
|
+
#start Worker
|
156
|
+
worker = @options[:worker] || 'fork' #todo make this line windows safe
|
157
|
+
require "updater/#{worker}_worker"
|
158
|
+
worker_class = Updater.const_get("#{worker.capitalize}Worker")
|
159
|
+
worker_class.logger = @logger
|
160
|
+
@logger.info "Using #{worker_class.to_s} to run jobs:"
|
161
|
+
worker_class.start(@options)
|
162
|
+
File.unlink(@options[:pid_file])
|
163
|
+
File.unlink @options[:socket] if @options[:socket] && File.exists?(@options[:socket])
|
164
|
+
end
|
165
|
+
|
166
|
+
def load_file(file)
|
167
|
+
return {} if file.nil?
|
168
|
+
file = File.open(file) if file.kind_of?(String)
|
169
|
+
@config_file = File.expand_path(file.path)
|
170
|
+
YAML.load(ERB.new(file.read).result(binding)) || {}
|
171
|
+
ensure
|
172
|
+
file.close if file.kind_of?(IO) && !file.closed?
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# This file based the file of the same name in the delayed_job gem by
|
2
|
+
# Tobias Luetke (Coypright (c) 2005) under the MIT License.
|
3
|
+
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
module Updater
|
7
|
+
|
8
|
+
#This class repeatedly searches the database for active jobs and runs them
|
9
|
+
class ThreadWorker
|
10
|
+
cattr_accessor :logger
|
11
|
+
attr_accessor :pid
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
def initialize(options={})
|
15
|
+
@quiet = options[:quiet]
|
16
|
+
@name = options[:name] || "host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
|
17
|
+
@pid = Process.pid
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
say "*** Starting job worker #{@name}"
|
22
|
+
@t = run_job_loop
|
23
|
+
|
24
|
+
trap('TERM') { terminate_with @t }
|
25
|
+
trap('INT') { terminate_with @t }
|
26
|
+
|
27
|
+
trap('USR1') do
|
28
|
+
old_proc = trap('USR1','IGNORE')
|
29
|
+
run_loop
|
30
|
+
trap('USR1',old_proc)
|
31
|
+
end
|
32
|
+
|
33
|
+
Thread.pass
|
34
|
+
|
35
|
+
sleep unless $exit
|
36
|
+
end
|
37
|
+
|
38
|
+
def say(text)
|
39
|
+
puts text unless @quiet
|
40
|
+
logger.info text if logger
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop
|
44
|
+
raise RuntimeError unless @t
|
45
|
+
terminate_with @t
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_loop
|
49
|
+
if @t.alive?
|
50
|
+
@t.wakeup #calling run here is a Bad Idea
|
51
|
+
else
|
52
|
+
say " ~~ Restarting Job Loop"
|
53
|
+
@t = run_job_loop
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def run_job_loop
|
60
|
+
Thread.new do
|
61
|
+
loop do
|
62
|
+
begin
|
63
|
+
delay = Update.work_off(self)
|
64
|
+
break if $exit
|
65
|
+
if delay
|
66
|
+
sleep delay
|
67
|
+
else
|
68
|
+
sleep
|
69
|
+
end
|
70
|
+
break if $exit
|
71
|
+
rescue
|
72
|
+
say "Caught exception in Job Loop"
|
73
|
+
sleep 0.1
|
74
|
+
retry
|
75
|
+
end
|
76
|
+
end
|
77
|
+
say "Worker thread exiting!"
|
78
|
+
Update.clear_locks(self)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def terminate_with(t)
|
83
|
+
say "Exiting..."
|
84
|
+
$exit = true
|
85
|
+
t.run if t.alive?
|
86
|
+
say "Forcing Shutdown" unless status = t.join(15) #Nasty inline assignment
|
87
|
+
Update.clear_locks(self)
|
88
|
+
exit status ? 0 : 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/lib/updater/update.rb
CHANGED
@@ -7,13 +7,14 @@ module Updater
|
|
7
7
|
# Contains the Error class after an error is caught in +run+. Not stored to the database
|
8
8
|
attr_reader :error
|
9
9
|
attr_reader :orm
|
10
|
+
attr_accessor :params
|
10
11
|
|
11
12
|
#Run the action on this traget compleating any chained actions
|
12
|
-
def run(job=nil
|
13
|
+
def run(job=nil)
|
13
14
|
ret = true #put return in scope
|
14
15
|
begin
|
15
16
|
t = target
|
16
|
-
final_args = sub_args(job
|
17
|
+
final_args = sub_args(job,@orm.method_args)
|
17
18
|
t.send(@orm.method.to_sym,*final_args)
|
18
19
|
rescue => e
|
19
20
|
@error = e
|
@@ -24,7 +25,8 @@ module Updater
|
|
24
25
|
run_chain :ensure
|
25
26
|
begin
|
26
27
|
@orm.destroy unless @orm.persistant
|
27
|
-
rescue
|
28
|
+
rescue StandardError => e
|
29
|
+
raise e unless e.class.to_s =~ /Connection/
|
28
30
|
sleep 0.1
|
29
31
|
retry
|
30
32
|
end
|
@@ -43,6 +45,7 @@ module Updater
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def initialize(orm_inst)
|
48
|
+
raise ArgumentError if orm_inst.nil?
|
46
49
|
@orm = orm_inst
|
47
50
|
end
|
48
51
|
|
@@ -75,14 +78,14 @@ module Updater
|
|
75
78
|
|
76
79
|
private
|
77
80
|
|
78
|
-
def sub_args(job,
|
81
|
+
def sub_args(job,a)
|
79
82
|
a.map do |e|
|
80
83
|
begin
|
81
84
|
case e.to_s
|
82
85
|
when '__job__'
|
83
86
|
job
|
84
87
|
when '__params__'
|
85
|
-
params
|
88
|
+
@params
|
86
89
|
when '__self__'
|
87
90
|
self
|
88
91
|
else
|
@@ -102,7 +105,7 @@ module Updater
|
|
102
105
|
chains = @orm.send(name)
|
103
106
|
return unless chains
|
104
107
|
chains.each do |job|
|
105
|
-
|
108
|
+
job.run(self)
|
106
109
|
end
|
107
110
|
rescue NameError
|
108
111
|
puts @orm.inspect
|
@@ -123,13 +126,21 @@ module Updater
|
|
123
126
|
# This is an open IO socket that will be writen to when a job is scheduled. If it is unset
|
124
127
|
# then @pid is signaled instead.
|
125
128
|
attr_accessor :socket
|
129
|
+
attr_writer :logger
|
130
|
+
|
131
|
+
def logger
|
132
|
+
@logger ||= Logger.new(STDOUT)
|
133
|
+
end
|
126
134
|
|
127
135
|
#Gets a single job form the queue, locks and runs it. it returns the number of second
|
128
136
|
#Until the next job is scheduled, or 0 is there are more current jobs, or nil if there
|
129
137
|
#are no jobs scheduled.
|
130
138
|
def work_off(worker)
|
131
139
|
inst = @orm.lock_next(worker)
|
132
|
-
|
140
|
+
if inst
|
141
|
+
worker.logger.debug " running job #{inst.id}"
|
142
|
+
new(inst).run
|
143
|
+
end
|
133
144
|
@orm.queue_time
|
134
145
|
ensure
|
135
146
|
clear_locks(worker)
|
@@ -346,7 +357,7 @@ module Updater
|
|
346
357
|
|
347
358
|
# Given some instance return the information needed to recreate that target
|
348
359
|
def target_for(inst)
|
349
|
-
return [inst, nil, nil] if inst.kind_of?
|
360
|
+
return [inst, nil, nil] if (inst.kind_of?(Class) || inst.kind_of?(Module))
|
350
361
|
[inst.class,@orm::FINDER,inst.send(orm::ID)]
|
351
362
|
end
|
352
363
|
|
data/spec/spec_helper.rb
CHANGED
@@ -3,11 +3,12 @@ require "rubygems"
|
|
3
3
|
ROOT = File.join(File.dirname(__FILE__), '..')
|
4
4
|
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
|
5
5
|
|
6
|
-
require "
|
6
|
+
require "rspec" # Satisfies Autotest and anyone else not using the Rake tasks
|
7
7
|
require "dm-core"
|
8
|
+
require 'dm-migrations'
|
8
9
|
|
9
10
|
require 'updater'
|
10
|
-
require 'updater/thread_worker'
|
11
|
+
#require 'updater/thread_worker'
|
11
12
|
require 'updater/fork_worker'
|
12
13
|
require 'updater/orm/datamapper'
|
13
14
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
ROOT = File.join(File.dirname(__FILE__), '..')
|
4
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
|
5
|
+
|
6
|
+
require "rspec" # Satisfies Autotest and anyone else not using the Rake tasks
|
7
|
+
require "dm-core"
|
8
|
+
require 'dm-migrations'
|
9
|
+
|
10
|
+
require 'updater'
|
11
|
+
#require 'updater/thread_worker'
|
12
|
+
require 'updater/fork_worker'
|
13
|
+
require 'updater/orm/datamapper'
|
14
|
+
|
15
|
+
Updater::Update.orm = Updater::ORM::DataMapper
|
16
|
+
|
17
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
18
|
+
DataMapper.auto_migrate!
|
19
|
+
|
20
|
+
require 'timecop'
|
21
|
+
require 'chronic'
|
22
|
+
|
23
|
+
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 3
|
9
|
+
version: 0.9.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- John F. Miller
|
@@ -14,13 +14,14 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-08-25 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: datamapper
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
@@ -35,6 +36,7 @@ dependencies:
|
|
35
36
|
name: rspec
|
36
37
|
prerelease: false
|
37
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
38
40
|
requirements:
|
39
41
|
- - "="
|
40
42
|
- !ruby/object:Gem::Version
|
@@ -49,6 +51,7 @@ dependencies:
|
|
49
51
|
name: timecop
|
50
52
|
prerelease: false
|
51
53
|
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
52
55
|
requirements:
|
53
56
|
- - ">="
|
54
57
|
- !ruby/object:Gem::Version
|
@@ -63,6 +66,7 @@ dependencies:
|
|
63
66
|
name: chronic
|
64
67
|
prerelease: false
|
65
68
|
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
66
70
|
requirements:
|
67
71
|
- - ">="
|
68
72
|
- !ruby/object:Gem::Version
|
@@ -89,7 +93,9 @@ files:
|
|
89
93
|
- Rakefile
|
90
94
|
- VERSION
|
91
95
|
- lib/updater.rb
|
96
|
+
- lib/updater/thread_worker.rb~
|
92
97
|
- lib/updater/setup.rb
|
98
|
+
- lib/updater/setup.rb~
|
93
99
|
- lib/updater/util.rb
|
94
100
|
- lib/updater/update.rb
|
95
101
|
- lib/updater/fork_worker.rb
|
@@ -97,12 +103,15 @@ files:
|
|
97
103
|
- lib/updater/simulated.db
|
98
104
|
- lib/updater/tasks.rb
|
99
105
|
- lib/updater/thread_worker.rb
|
106
|
+
- lib/updater/orm/mongo.rb~
|
100
107
|
- lib/updater/orm/orm.rb
|
101
108
|
- lib/updater/orm/datamapper.rb
|
109
|
+
- lib/updater/orm/mongo.rb
|
102
110
|
- spec/fork_worker_instance_spec.rb
|
103
111
|
- spec/thread_worker_spec.rb
|
104
112
|
- spec/schedule_spec.rb
|
105
113
|
- spec/lock_spec.rb
|
114
|
+
- spec/spec_helper.rb~
|
106
115
|
- spec/params_sub_spec.rb
|
107
116
|
- spec/chained_spec.rb
|
108
117
|
- spec/fooclass.rb
|
@@ -124,6 +133,7 @@ rdoc_options: []
|
|
124
133
|
require_paths:
|
125
134
|
- lib
|
126
135
|
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
127
137
|
requirements:
|
128
138
|
- - ">="
|
129
139
|
- !ruby/object:Gem::Version
|
@@ -131,6 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
131
141
|
- 0
|
132
142
|
version: "0"
|
133
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
134
145
|
requirements:
|
135
146
|
- - ">="
|
136
147
|
- !ruby/object:Gem::Version
|
@@ -140,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
151
|
requirements: []
|
141
152
|
|
142
153
|
rubyforge_project:
|
143
|
-
rubygems_version: 1.3.
|
154
|
+
rubygems_version: 1.3.7
|
144
155
|
signing_key:
|
145
156
|
specification_version: 3
|
146
157
|
summary: A Gem for queuing methods for later calling which is ORM Agnostic, and has advanced Error Handling
|