Class: Updater::Update
- Inherits:
-
Object
- Object
- Updater::Update
- Defined in:
- lib/updater/update.rb
Overview
The basic class that drives Updater. See Readme for usage information.
Class Attribute Summary collapse
-
.config_file ⇒ Object
The name of the file to look for information if we loose the server.
-
.finder_id ⇒ Object
This is the application level default method to call on an instance type target.
-
.finder_method ⇒ Object
This is the application level default method to call on a class in order to find/create a target instance.
-
.logger ⇒ Object
Returns the logger instance.
-
.orm ⇒ Object
This attribute must be set to some ORM that will persist the data.
-
.socket ⇒ Object
This is an open IO socket that will be writen to when a job is scheduled.
Instance Attribute Summary collapse
-
#error ⇒ Object
readonly
Contains the Error class after an error is caught in
run
. -
#orm ⇒ Object
readonly
Contains the underlying ORM instance (eg. ORM::Datamapper or ORM Mongo).
-
#params ⇒ Object
In order to reduce the proliferation of chained jobs in the queue, jobs chain request are allowed a params value that will pass specific values to a chained method.
Class Method Summary collapse
-
.at(t, target, method = nil, args = [], options = {}) ⇒ Object
Request that the target be sent the method with args at the given time.
-
.chain(*args) ⇒ Object
like
at
but without a time to run. -
.clear_all ⇒ Object
Remove all scheduled jobs.
-
.clear_locks(worker) ⇒ Object
Ensure that a worker no longer holds any locks.
-
.current ⇒ Object
A filter for all requests that are ready to run, that is they requested to be run before or at time.now and ar not being processed by another worker.
-
.delayed ⇒ Object
A count of how many jobs are scheduled but not yet run.
-
.for(target, name = nil) ⇒ Object
Retrieves all updates for a conforming target possibly limiting the results to the named request.
-
.future(start, finish = nil) ⇒ Object
How many jobs will happen at least ‘start’ seconds from now, but not more then finish seconds from now.
-
.immidiate(*args) ⇒ Object
like
at
but with time as time.now. -
.in(t, *args) ⇒ Object
Run this job in ‘time’ seconds from now.
-
.load ⇒ Object
The number of jobs currently backloged in the system.
-
.pid ⇒ Object
The PID of the worker process.
-
.pid=(p) ⇒ Object
Sets the process id of the worker process if known.
-
.schedule(hash) ⇒ Object
Advanced: This method allows values to be passed directly to the ORM layer’s create method.
-
.time ⇒ Object
The time class used by Updater.
-
.time=(klass) ⇒ Object
By default Updater will use the system time (Time class) to get the current time.
-
.work_off(worker) ⇒ Object
Gets a single job form the queue, locks and runs it.
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#id ⇒ Object
This is the appropriate value to use for a chanable field value.
-
#initialize(orm_inst) ⇒ Update
constructor
orm_inst must be set to an instacne of the class Update.orm.
- #inspect ⇒ Object
- #method ⇒ Object
-
#method_missing(method, *args) ⇒ Object
see if this method was intended for the underlying ORM layer.
-
#name ⇒ Object
Jobs may be named to make them easier to find.
-
#name=(n) ⇒ Object
Jobs may be named to make them easier to find.
-
#persistant? ⇒ Boolean
If this is true, the job will NOT be removed after it is run.
-
#run(job = nil) ⇒ Object
Run the action on this traget compleating any chained actions.
-
#target ⇒ Object
Determins and if necessary find/creates the target for this instance.
Constructor Details
#initialize(orm_inst) ⇒ Update
orm_inst must be set to an instacne of the class Update.orm
70 71 72 73 74 75 76 |
# File 'lib/updater/update.rb', line 70 def initialize(orm_inst) if orm_inst.nil? || !orm_inst.kind_of?(self.class.orm) raise ArgumentError, "Update has been set to use %s but recieved a %s (%s:%s)\n recieved %s." % [self.class.orm.inspect, orm_inst.class.inspect, __FILE__,__LINE__-1,orm_inst.inspect] end @orm = orm_inst end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args) ⇒ Object
see if this method was intended for the underlying ORM layer.
46 47 48 |
# File 'lib/updater/update.rb', line 46 def method_missing(method, *args) @orm.send(method,*args) end |
Class Attribute Details
.config_file ⇒ Object
The name of the file to look for information if we loose the server
414 415 416 |
# File 'lib/updater/update.rb', line 414 def config_file @config_file end |
.finder_id ⇒ Object
This is the application level default method to call on an instance type target. It should return a value to be passed to the #finder_method (above) inorder to retrieve the instance from the datastore. (eg. id) In most circumstances the ORM layer defines an appropriate default and this does not need to be explcitly set.
MongoDB is one significant exception to this rule. The Updater Mongo ORM layer uses the 10gen MongoDB dirver directly without an ORM such as Mongoid or Mongo_Mapper. If the application uses ond of thes ORMs #finder_method and #finder_id should be explicitly set.
197 198 199 |
# File 'lib/updater/update.rb', line 197 def finder_id @finder_id end |
.finder_method ⇒ Object
This is the application level default method to call on a class in order to find/create a target instance. (e.g find, get, find_one, etc…). In most circumstances the ORM layer defines an appropriate default and this does not need to be explcitly set.
MongoDB is one significant exception to this rule. The Updater Mongo ORM layer uses the 10gen MongoDB dirver directly without an ORM such as Mongoid or Mongo_Mapper. If the application uses one of thes ORMs #finder_method and #finder_id should be explicitly set.
187 188 189 |
# File 'lib/updater/update.rb', line 187 def finder_method @finder_method end |
.logger ⇒ Object
Returns the logger instance. If it has not been set, a new Logger will be created pointing to STDOUT
214 215 216 |
# File 'lib/updater/update.rb', line 214 def logger @logger ||= Logger.new(STDOUT) end |
.orm ⇒ Object
This attribute must be set to some ORM that will persist the data. The value is normally set using one of the methods in Updater::Setup.
178 179 180 |
# File 'lib/updater/update.rb', line 178 def orm @orm end |
.socket ⇒ Object
This is an open IO socket that will be writen to when a job is scheduled. If it is unset then @pid is signaled instead.
208 209 210 |
# File 'lib/updater/update.rb', line 208 def socket @socket end |
Instance Attribute Details
#error ⇒ Object (readonly)
Contains the Error class after an error is caught in run
. Not stored to the database
8 9 10 |
# File 'lib/updater/update.rb', line 8 def error @error end |
#orm ⇒ Object (readonly)
Contains the underlying ORM instance (eg. ORM::Datamapper or ORM Mongo)
11 12 13 |
# File 'lib/updater/update.rb', line 11 def orm @orm end |
#params ⇒ Object
In order to reduce the proliferation of chained jobs in the queue, jobs chain request are allowed a params value that will pass specific values to a chained method. When a chained instance is created, the job processor will set this value. It will then be sent to the target method in plance of ‘__param__’. See #sub_args
18 19 20 |
# File 'lib/updater/update.rb', line 18 def params @params end |
Class Method Details
.at(t, target, method = nil, args = [], options = {}) ⇒ Object
Request that the target be sent the method with args at the given time.
Parameters
time <Integer | Object responding to to_i>, by default the number of seconds sence the epoch.
What ‘time’ references can be set by sending the a substitute class to the time= method.
target <Class | instance> . If target is a class then ‘method’ will be sent to that class (unless the finder option is used. Otherwise, the target will be assumed to be the result of (target.class).get(target.id). (note: The ORM can/should override #get and #id with the proper methods for it’s storage model.) The finder method (:get by default) and the finder_args (target.id by default) can be set in the options. A ORM (eg DataMapper) instance passed as the target will “just work.” Any object can be found in this mannor is known as a ‘conforming instance’. TODO: make ORM finder and id constants overridable for times when one ORM is used for Updater and another is used by the model classes.
method <Symbol>. The method that will be sent to the calculated target.
args <Array> a list of arguments to be sent to with the method call. Note: ‘args’ must be seirialiable with Marshal.dump. The special values ‘__job__’, ‘__params__’, and ‘__self__’ are replaced they are found in this list. Defaults to []. (note: the #to_s method will be called on all args before variable substitution any arg that responds with one of the special values will be replaced as noted above. E.g :__job__ . If something is silly enough to respond to to_s with a non-pure method you will have problems. NoMethodError is caught and handled gracefully)
options <Hash> Addational options that will be used to configure the request. see Options section below.
Options
:finder <Symbol> This method will be sent to the stored target class (either target or target.class) inorder to extract the instance on which to preform the request. By default :get is used. For example to use on an ActiveRecord class
:finder=>:find
:finder_args <Array> | <Object>. This is passed to the finder function. By default it is target.id. Note that by setting :finder_args you will force Updater to calculate in instance as the computed target even if you pass a Class as the target.
:name <String> A string sent by the requesting class to identify the request. ‘name’ must be unique for a given computed target. Names cannot be used effectivally when a Class has non- conforming instances as there is no way predict the results of a finder call. ‘name’ can be used in conjunction with the for
method to manipulate requests effecting an object or class after they are set. See for
for examples
:failure, :success,:ensure <Updater::Update instance> an other request to be run when the request compleste. Usually these valuses will be created with the chained
method.
As an alternative a Hash (OrderedHash in ruby 1.8) with keys of Updater::Update instances and values of Hash may be used. The hash will be substituted for the ‘__param__’ argument if/when the chained method is called.
:persistant <true|false> if true the object will not be destroyed after the completion of its run. By default this is false except when time is nil.
Note:
Unless finder_args is passed, a non-class target will be asked for its ID value using #finder_id or if that is not set, then the default value defined in the ORM layer. Particularly for MongoDB it is important that #finder_id be set to an appropriate value sence the Updater ORM layer uses the low level MongoDB driver instead of a more feature complete ORM like Mongoid.
Examples
Updater.at(Chronic.parse('tomorrow'),Foo,:bar,[]) # will run Foo.bar() tomorrow at midnight
f = Foo.create
u = Updater.at(Chronic.parse('2 hours form now'),f,:bar,[]) # will run Foo.get(f.id).bar in 2 hours
See Also
in
, immidiate
and chain
which share the same arguments and options but treat time differently
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/updater/update.rb', line 305 def at(t,target,method = nil,args=[],={}) hash = Hash.new hash[:time] = t.to_i unless t.nil? hash[:target],hash[:finder],hash[:finder_args] = target_for(target, ) hash[:method] = method || :perform hash[:method_args] = args [:name,:failure,:success,:ensure].each do |opt| hash[opt] = [opt] if [opt] end hash[:persistant] = [:persistant] || t.nil? ? true : false schedule(hash) end |
.chain(*args) ⇒ Object
like at
but without a time to run. This is used to create requests that run in responce to the failure of other requests. See at
for details
347 348 349 |
# File 'lib/updater/update.rb', line 347 def chain(*args) at(nil,*args) end |
.clear_all ⇒ Object
Remove all scheduled jobs. Mostly intended for testing, but may also be useful in cases of crashes or system corruption. removes all pending jobs.
409 410 411 |
# File 'lib/updater/update.rb', line 409 def clear_all @orm.clear_all end |
.clear_locks(worker) ⇒ Object
Ensure that a worker no longer holds any locks.
235 |
# File 'lib/updater/update.rb', line 235 def clear_locks(worker); @orm.clear_locks(worker); end |
.current ⇒ Object
A filter for all requests that are ready to run, that is they requested to be run before or at time.now and ar not being processed by another worker
386 387 388 |
# File 'lib/updater/update.rb', line 386 def current @orm.current end |
.delayed ⇒ Object
A count of how many jobs are scheduled but not yet run
396 397 398 |
# File 'lib/updater/update.rb', line 396 def delayed @orm.delayed end |
.for(target, name = nil) ⇒ Object
Retrieves all updates for a conforming target possibly limiting the results to the named request.
Parameters
target <Class | Object> a class or conforming object that postentially is the calculated target of a request.
name(optional) <String> If a name is sent, the first request with fot this target with this name will be returned.
Returns
<Array> unless name is given then only a single [Updater] instance.
365 366 367 368 369 |
# File 'lib/updater/update.rb', line 365 def for(target,name=nil) target,finder,args = target_for(target) ret = @orm.for(target,finder,args,name).map {|i| new(i)} name ? ret.first : ret end |
.future(start, finish = nil) ⇒ Object
How many jobs will happen at least ‘start’ seconds from now, but not more then finish seconds from now. If the second parameter is nil then it is the number of jobbs between now and the first parameter.
402 403 404 405 |
# File 'lib/updater/update.rb', line 402 def future(start,finish = nil) start, finish = [0, start] unless finish @orm.future(start,finish) end |
.immidiate(*args) ⇒ Object
like at
but with time as time.now. Generally this will be used to run a long running operation in asyncronously in a differen process. See at
for details
341 342 343 |
# File 'lib/updater/update.rb', line 341 def immidiate(*args) at(time.now,*args) end |
.in(t, *args) ⇒ Object
Run this job in ‘time’ seconds from now. See at
for details on expected args.
324 325 326 |
# File 'lib/updater/update.rb', line 324 def in(t,*args) at(time.now+t,*args) end |
.load ⇒ Object
The number of jobs currently backloged in the system
391 392 393 |
# File 'lib/updater/update.rb', line 391 def load @orm.current_load end |
.pid ⇒ Object
The PID of the worker process
436 437 438 |
# File 'lib/updater/update.rb', line 436 def pid @pid end |
.pid=(p) ⇒ Object
Sets the process id of the worker process if known. If this is set then an attempt will be made to signal the worker any time a new update is made.
The PID will not be signaled if @socket is availible, but should be set as a back-up
If pid is not set, or is set to nil then the scheduleing program is responcible for waking-up a potentially sleeping worker process in another way.
425 426 427 428 429 430 431 432 433 |
# File 'lib/updater/update.rb', line 425 def pid=(p) return @pid = nil unless p #tricky assignment in return @pid = Integer("#{p}") #safety check that prevents a curupted PID file from crashing the system Process::kill 0, @pid #check that the process exists @pid rescue Errno::ESRCH, ArgumentError @pid = nil raise ArgumentError, "PID was invalid" end |
.schedule(hash) ⇒ Object
Advanced: This method allows values to be passed directly to the ORM layer’s create method. use at
and friends for everyday use cases.
330 331 332 333 334 335 336 337 |
# File 'lib/updater/update.rb', line 330 def schedule(hash) r = new(@orm.create(hash)) signal_worker r rescue NoMethodError raise ArgumentError, "ORM not initialized!" if @orm.nil? raise end |
.time ⇒ Object
The time class used by Updater. See time=
372 373 374 |
# File 'lib/updater/update.rb', line 372 def time @time ||= Time end |
.time=(klass) ⇒ Object
By default Updater will use the system time (Time class) to get the current time. The application that Updater was developed for used a game clock that could be paused or restarted. This method allows us to substitute a custom class for Time. This class must respond with in interger or Time to the #now method.
380 381 382 |
# File 'lib/updater/update.rb', line 380 def time=(klass) @time = klass end |
.work_off(worker) ⇒ Object
Gets a single job form the queue, locks and runs it. it returns the number of second Until the next job is scheduled, or 0 is there are more current jobs, or nil if there are no jobs scheduled.
221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/updater/update.rb', line 221 def work_off(worker) inst = @orm.lock_next(worker) if inst worker.logger.debug " running job #{inst.id}" new(inst).run else worker.logger.debug " could not find a ready job in the datastore" end @orm.queue_time ensure clear_locks(worker) end |
Instance Method Details
#==(other) ⇒ Object
93 94 95 |
# File 'lib/updater/update.rb', line 93 def ==(other) other.kind_of?(self.class) && id == other.id end |
#id ⇒ Object
This is the appropriate value to use for a chanable field value
89 90 91 |
# File 'lib/updater/update.rb', line 89 def id @orm.id end |
#inspect ⇒ Object
102 103 104 105 106 |
# File 'lib/updater/update.rb', line 102 def inspect "#<Updater::Update target=#{target.inspect} time=#{orm.time}>" rescue TargetMissingError "#<Updater::Update target=<missing> time=#{orm.time}>" end |
#method ⇒ Object
50 51 52 |
# File 'lib/updater/update.rb', line 50 def method @orm.method end |
#name ⇒ Object
Jobs may be named to make them easier to find
84 85 86 |
# File 'lib/updater/update.rb', line 84 def name @orm.name end |
#name=(n) ⇒ Object
Jobs may be named to make them easier to find
79 80 81 |
# File 'lib/updater/update.rb', line 79 def name=(n) @orm.name=n end |
#persistant? ⇒ Boolean
If this is true, the job will NOT be removed after it is run. This is usually true for chained Jobs.
98 99 100 |
# File 'lib/updater/update.rb', line 98 def persistant? @orm.persistant end |
#run(job = nil) ⇒ Object
Run the action on this traget compleating any chained actions
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/updater/update.rb', line 21 def run(job=nil) ret = true #put return in scope begin t = target final_args = sub_args(job,@orm.method_args) t.send(@orm.method.to_sym,*final_args) rescue => e @error = e run_chain :failure ret = false ensure run_chain :success if ret run_chain :ensure begin @orm.destroy unless @orm.persistant rescue StandardError => e raise e unless e.class.to_s =~ /Connection/ sleep 0.1 retry end end ret end |
#target ⇒ Object
Determins and if necessary find/creates the target for this instance.
Warning: This value is intentionally NOT memoized. For instance type targets, it will result in a call to the datastore (or the recreation of an object) on EACH invocation. Methods that need to refer to the target more then once should take care to store this value locally after initial retreavel.
59 60 61 62 63 64 65 66 67 |
# File 'lib/updater/update.rb', line 59 def target begin target = @orm.finder.nil? ? @orm.target : @orm.target.send(@orm.finder,*@orm.finder_args) raise TargetMissingError unless target rescue raise TargetMissingError, "Target missing --Class:'#{@orm.target}' Finder:'#{@orm.finder}', Args:'#{@orm.finder_args.inspect}'" end target end |