updater 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|