queue_dispatcher 1.1.0

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.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +55 -0
  5. data/Rakefile +2 -0
  6. data/app/assets/images/icon_acquire_lock.png +0 -0
  7. data/app/assets/images/icon_error.png +0 -0
  8. data/app/assets/images/icon_init_queue.gif +0 -0
  9. data/app/assets/images/icon_pending.gif +0 -0
  10. data/app/assets/images/icon_running.gif +0 -0
  11. data/app/assets/images/icon_successful.png +0 -0
  12. data/app/assets/images/icon_warning.gif +0 -0
  13. data/app/assets/images/lock.png +0 -0
  14. data/app/assets/javascript/tasks.js.coffee +8 -0
  15. data/app/helpers/tasks_helper.rb +28 -0
  16. data/app/models/task_dependency.rb +2 -0
  17. data/app/views/queue_dispatcher_views/_search_results_events.html.haml +4 -0
  18. data/app/views/queue_dispatcher_views/_search_results_my_events.html.haml +4 -0
  19. data/app/views/queue_dispatcher_views/_task_event.html.haml +24 -0
  20. data/app/views/queue_dispatcher_views/_task_footer.html.erb +1 -0
  21. data/app/views/queue_dispatcher_views/_task_header.html.erb +12 -0
  22. data/app/views/queue_dispatcher_views/_task_my_event.html.haml +24 -0
  23. data/app/views/queue_dispatcher_views/expand_event.js.rjs +1 -0
  24. data/app/views/queue_dispatcher_views/my_events.html.haml +7 -0
  25. data/app/views/queue_dispatcher_views/my_events.js.erb +6 -0
  26. data/app/views/queue_dispatcher_views/update_events.js.erb +15 -0
  27. data/lib/generators/queue_dispatcher/migration/migration_generator.rb +51 -0
  28. data/lib/generators/queue_dispatcher/migration/templates/task_dependencies.rb +16 -0
  29. data/lib/generators/queue_dispatcher/migration/templates/task_queues.rb +18 -0
  30. data/lib/generators/queue_dispatcher/migration/templates/tasks.rb +25 -0
  31. data/lib/queue_dispatcher/acts_as_task.rb +194 -0
  32. data/lib/queue_dispatcher/acts_as_task_controller.rb +95 -0
  33. data/lib/queue_dispatcher/acts_as_task_queue.rb +447 -0
  34. data/lib/queue_dispatcher/deserialization_error.rb +4 -0
  35. data/lib/queue_dispatcher/message_sending.rb +31 -0
  36. data/lib/queue_dispatcher/psych_ext.rb +127 -0
  37. data/lib/queue_dispatcher/qd_logger.rb +15 -0
  38. data/lib/queue_dispatcher/rc_and_msg.rb +60 -0
  39. data/lib/queue_dispatcher/serialization/active_record.rb +20 -0
  40. data/lib/queue_dispatcher/syck_ext.rb +34 -0
  41. data/lib/queue_dispatcher/version.rb +3 -0
  42. data/lib/queue_dispatcher/yaml_ext.rb +10 -0
  43. data/lib/queue_dispatcher.rb +20 -0
  44. data/lib/tasks/queue_dispatcher.rake +7 -0
  45. data/queue_dispatcher.gemspec +26 -0
  46. data/script/queue_worker_dispatcher +259 -0
  47. metadata +187 -0
@@ -0,0 +1,447 @@
1
+ require 'sys/proctable'
2
+ require 'queue_dispatcher/rc_and_msg'
3
+ require 'spawn'
4
+
5
+ module QueueDispatcher
6
+ module ActsAsTaskQueue
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+
12
+ class Config
13
+ attr_reader :task_class_name
14
+ attr_reader :leave_running_tasks_in_queue
15
+ attr_reader :leave_finished_tasks_in_queue
16
+ attr_reader :idle_wait_time
17
+ attr_reader :poll_time
18
+
19
+ def initialize(args)
20
+ @task_class_name = (args[:task_model] || :task).to_s.underscore
21
+ @leave_finished_tasks_in_queue = args[:leave_finished_tasks_in_queue].nil? ? false : args[:leave_finished_tasks_in_queue]
22
+ @leave_running_tasks_in_queue = args[:leave_running_tasks_in_queue].nil? ? false : args[:leave_running_tasks_in_queue]
23
+ @leave_running_tasks_in_queue = true if @leave_finished_tasks_in_queue
24
+ @idle_wait_time = args[:idle_wait_time] || 0
25
+ @poll_time = args[:poll_time] || 2.seconds
26
+ end
27
+ end
28
+
29
+
30
+ module ClassMethods
31
+ def acts_as_task_queue args = {}
32
+ include Spawn
33
+ include ActionView::Helpers::UrlHelper
34
+ include QdLogger
35
+
36
+ include QueueDispatcher::ActsAsTaskQueue::InstanceMethods
37
+ extend QueueDispatcher::ActsAsTaskQueue::SingletonMethods
38
+
39
+ @acts_as_task_queue_config = QueueDispatcher::ActsAsTaskQueue::Config.new(args)
40
+
41
+ has_many acts_as_task_queue_config.task_class_name.pluralize, :order => [:priority, :id]
42
+ end
43
+ end
44
+
45
+
46
+ module SingletonMethods
47
+ def acts_as_task_queue_config
48
+ @acts_as_task_queue_config
49
+ end
50
+
51
+
52
+ # Are there any running task_queues?
53
+ def any_running?
54
+ running = false
55
+ all.each{ |tq| running = true if tq.running? || tq.brand_new? }
56
+ running
57
+ end
58
+
59
+
60
+ # Get next pending task_queue
61
+ def get_next_pending
62
+ task_queue = nil
63
+
64
+ transaction do
65
+ # Find next task_queue which is not running and not in state error
66
+ order(:id).lock(true).all.each { |tq| task_queue = tq unless task_queue || tq.pid_running? || tq.state == 'error' }
67
+
68
+ # Update pid inside the atomic transaction to be sure, the next call of this method will not give the same queue a second time
69
+ task_queue.update_attribute :pid, $$ if task_queue
70
+ end
71
+
72
+ task_queue
73
+ end
74
+
75
+
76
+ # Find or create a task_queue by its name which is not in state 'error'. Create one, if there does not exists one
77
+ def find_or_create_by_name name, options = {}
78
+ transaction do
79
+ self.where(:name => name).where('state != "error"').first || self.create(:name => name, :state => 'new', terminate_immediately: options[:terminate_immediately])
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ module InstanceMethods
86
+ def acts_as_task_queue_tasks
87
+ self.send(acts_as_task_queue_config.task_class_name.pluralize)
88
+ end
89
+
90
+
91
+ # Put a new task into the queue
92
+ def push task
93
+ acts_as_task_queue_tasks << task
94
+ end
95
+
96
+
97
+ # Get the next ready to run task out of the queue. Consider the priority and the dependent tasks, which is defined in the association defined on
98
+ # top of this model.
99
+ def pop args = {}
100
+ task = nil
101
+
102
+ transaction do
103
+ # Find next pending task, where all dependent tasks are executed
104
+ all_tasks = acts_as_task_queue_tasks.lock(true).all
105
+ i = 0
106
+ while task.nil? && i < all_tasks.count do
107
+ t = all_tasks[i]
108
+ if t.dependent_tasks_executed?
109
+ task = t if t.state == 'new'
110
+ else
111
+ log :msg => "Task #{t.id}: Waiting for dependent tasks #{t.dependent_tasks.map{|dt| dt.id}.join ','}...",
112
+ :sev => :debug
113
+ end
114
+ i += 1
115
+ end
116
+
117
+ # Remove task from current queue
118
+ if task
119
+ if args[:remove_task].nil? || args[:remove_task]
120
+ task.update_attribute :task_queue_id, nil
121
+ else
122
+ task.update_attribute :state, 'new_popped'
123
+ end
124
+ end
125
+ end
126
+
127
+ task
128
+ end
129
+
130
+
131
+ # Returns the state of this task list (:stopped or :running)
132
+ def task_states
133
+ states = determine_state_of_task_array acts_as_task_queue_tasks
134
+
135
+ if states[:empty]
136
+ nil
137
+ elsif states[:running]
138
+ :running
139
+ elsif states[:init_queue]
140
+ :init_queue
141
+ elsif states[:pending]
142
+ :pending
143
+ elsif states[:acquire_lock]
144
+ :acquire_lock
145
+ elsif states[:error]
146
+ :error
147
+ elsif states[:new]
148
+ :new
149
+ elsif states[:successful]
150
+ :successful
151
+ else
152
+ :unknown
153
+ end
154
+ end
155
+
156
+
157
+ # Return true, if the command of the process with pid 'self.pid' is 'ruby'
158
+ def pid_running?
159
+ ps = self.pid ? Sys::ProcTable.ps(self.pid) : nil
160
+ if ps
161
+ # Asume, that if the command of the 'ps'-output is 'ruby', the process is still running
162
+ ps.comm == 'ruby'
163
+ else
164
+ false
165
+ end
166
+ end
167
+
168
+
169
+ # Return true, if the task_queue is still running
170
+ def running?
171
+ state == 'running' && pid_running?
172
+ end
173
+
174
+
175
+ # Return true, if the task_queue is in state new and is not older 30 seconds
176
+ def brand_new?
177
+ state == 'new' && (Time.now - created_at) < 30.seconds
178
+ end
179
+
180
+
181
+ # Return true if there are no tasks in this taskqueue
182
+ def empty?
183
+ acts_as_task_queue_tasks.empty?
184
+ end
185
+
186
+
187
+ # Are there any running or pending tasks in the queue?
188
+ def pending_tasks?
189
+ transaction do
190
+ queue = TaskQueue.where(:id => self.id).lock(true).first
191
+ states = determine_state_of_task_array queue.acts_as_task_queue_tasks.lock(true)
192
+ states[:running] || states[:pending] || states[:acquire_lock] || states[:init_queue]
193
+ end
194
+ end
195
+
196
+
197
+ # Are all tasks executed?
198
+ def all_done?
199
+ ! pending_tasks? || empty?
200
+ end
201
+
202
+
203
+ # Return true, if the task_queue is working or has pending jobs
204
+ def working?
205
+ self.task_states == :running && self.running?
206
+ end
207
+
208
+
209
+ # Return true, if the task_queue has pending jobs and is running but no job is running
210
+ def pending?
211
+ ts = task_states
212
+ (ts == :new || ts == :pending || ts == :acquire_lock) && self.running?
213
+ end
214
+
215
+
216
+ # Kill a task_queue
217
+ def kill
218
+ Process.kill('HUP', pid) if pid
219
+ end
220
+
221
+
222
+ # Destroy the queue if it has no pending jobs
223
+ def destroy_if_all_done!
224
+ transaction do
225
+ queue = TaskQueue.where(:id => self.id).lock(true).first
226
+ queue.destroy if queue && queue.all_done?
227
+ end
228
+ end
229
+
230
+
231
+ # Remove finished tasks from queue
232
+ def remove_finished_tasks!
233
+ trasnaction do
234
+ tasks.each{ |t| t.update_attribute(:task_queue_id, nil) if t.executed? }
235
+ end
236
+ end
237
+
238
+
239
+ # Execute all tasks in the queue
240
+ def run! args = {}
241
+ task = nil
242
+ @logger = args[:logger] || Logger.new("#{File.expand_path(Rails.root)}/log/task_queue.log")
243
+ finish_state = 'aborted'
244
+ task_queue = self
245
+ print_log = args[:print_log]
246
+
247
+ task_queue.update_attribute :state, 'running'
248
+
249
+ # Set logger in engine
250
+ @engine.logger = @logger if defined? @engine
251
+ log :msg => "#{name}: Starting TaskQueue #{task_queue.id}...", :print_log => print_log
252
+
253
+ # Init. Pop first task from queue, to show init_queue-state
254
+ task = task_queue.pop(:remove_task => false)
255
+ task.update_attribute :state, 'init_queue' if task
256
+ init
257
+
258
+ # Put task, which was used for showing the init_queue-state, back into the task_queue
259
+ task.update_attributes :state => 'new', :task_queue_id => task_queue.id if task
260
+ task_queue.reload
261
+
262
+ # Ensure, that each task_queue is executed at least once, even if there are no tasks inside at the time it is started (this
263
+ # can happen, if there are a lot of DB activities...)
264
+ first_run = true
265
+ # Loop as long as the task_queue exists with states 'running' and until the task_queue has pending tasks
266
+ while task_queue && task_queue.state == 'running' && (task_queue.pending_tasks? || first_run) do
267
+ first_run = false
268
+
269
+ # Pop next task from queue
270
+ task = task_queue.pop(:remove_task => (! acts_as_task_queue_config.leave_running_tasks_in_queue))
271
+
272
+ if task
273
+ if task.new?
274
+ # Start
275
+ task.update_attributes :state => 'acquire_lock', :perc_finished => 0
276
+ get_lock_for task
277
+ log :msg => "#{name}: Starting task #{task.id} (#{task.target.class.name}.#{task.method_name})...", :print_log => print_log
278
+ task.update_attributes :state => 'running'
279
+
280
+ # Execute the method defined in task.method
281
+ if task.target.methods.include?(task.method_name) || task.target.methods.include?(task.method_name.to_sym)
282
+ if task.dependent_tasks_had_errors
283
+ error_msg = "Dependent tasks had errors!"
284
+ log :msg => error_msg,
285
+ :sev => :warn,
286
+ :print_log => print_log
287
+ rc_and_msg = QueueDispatcher::RcAndMsg.bad_rc error_msg
288
+ else
289
+ target = task.target
290
+ target.logger = @logger if target.methods.include?(:logger=) || target.methods.include?('logger=')
291
+ rc_and_msg = task.execute!
292
+ end
293
+ else
294
+ error_msg = "unknown method '#{task.method_name}' for #{task.target.class.name}!"
295
+ log :msg => error_msg,
296
+ :sev => :warn,
297
+ :print_log => print_log
298
+ rc_and_msg = QueueDispatcher::RcAndMsg.bad_rc error_msg
299
+ end
300
+
301
+ # Change task state according to the return code and remove it from the queue
302
+ task.update_state rc_and_msg
303
+ cleanup_locks_after_error_for task
304
+ task.update_attribute :task_queue_id, nil unless acts_as_task_queue_config.leave_finished_tasks_in_queue
305
+ log :msg => "#{name}: Task #{task.id} (#{task.target.class.name}.#{task.method_name}) finished with state '#{task.state}'.", :print_log => print_log
306
+ end
307
+ else
308
+ # We couldn't fetch a task out of the queue but there should still exists some. Maybe some are waiting for dependent tasks.
309
+ # Sleep some time before trying it again.
310
+ sleep acts_as_task_queue_config.poll_time
311
+ end
312
+
313
+ # Reload task_queue to get all updates
314
+ task_queue = TaskQueue.find_by_id task_queue.id
315
+
316
+ # If all tasks are finished, a config reload will be executed at the end of this method. To avoid too much config reloads,
317
+ # wait some time before continuing. Maybe, some more tasks will added to the queue?!
318
+ wait_time = 0
319
+ unless task_queue.nil? || task_queue.terminate_immediately
320
+ until task_queue.nil? || task_queue.pending_tasks? || wait_time >= acts_as_task_queue_config.idle_wait_time || task_queue.state != 'running' do
321
+ sleep acts_as_task_queue_config.poll_time
322
+ wait_time += acts_as_task_queue_config.poll_time
323
+ task_queue = TaskQueue.find_by_id task_queue.id
324
+ end
325
+ end
326
+
327
+ # Reset logger since this got lost by reloading the task_queue
328
+ task_queue.logger = @logger if task_queue
329
+ end
330
+
331
+ # Reload config if last task was not a config reload
332
+ config_reload_required = cleanup_before_auto_reload
333
+ if config_reload_required
334
+ task_queue.update_attributes :state => 'reloading_config' if task_queue
335
+ reload_config task, print_log: print_log
336
+ end
337
+
338
+ # Delete task_queue
339
+ task_queue.destroy_if_all_done! if task_queue
340
+
341
+ # Loop has ended
342
+ log :msg => "#{name}: TaskQueue has ended!", :print_log => print_log
343
+ finish_state = 'stopped'
344
+ rescue => exception
345
+ # Error handler
346
+ backtrace = exception.backtrace.join("\n ")
347
+ log :msg => "Fatal error in method 'run!': #{$!}\n #{backtrace}", :sev => :error, :print_log => print_log
348
+ puts "Fatal error in method 'run!': #{$!}\n#{backtrace}"
349
+ task.update_state QueueDispatcher::RcAndMsg.bad_rc("Fatal error: #{$!}") if task
350
+ cleanup_locks_after_error_for task if task
351
+ finish_state = 'error'
352
+ ensure
353
+ # Reload task and task_queue, to ensure the objects are up to date
354
+ task_queue = TaskQueue.find_by_id task_queue.id if task_queue
355
+ task = Task.find_by_id task.id if task
356
+
357
+ # Update states of task and task_queue
358
+ task.update_attributes :state => 'aborted' if task && task.state == 'running'
359
+ task_queue.update_attributes :state => finish_state, :pid => nil if task_queue
360
+
361
+ # Clean up
362
+ deinit
363
+ end
364
+
365
+
366
+ #----------------------------------------------------------------------------------------------------------------
367
+ private
368
+ #----------------------------------------------------------------------------------------------------------------
369
+
370
+
371
+ def acts_as_task_queue_config
372
+ self.class.acts_as_task_queue_config
373
+ end
374
+
375
+
376
+ def determine_state_of_task_array task_array
377
+ successful = true
378
+ new = true
379
+ pending = false
380
+ error = false
381
+ running = false
382
+ acquire_lock = false
383
+ init_queue = false
384
+
385
+ task_array.each do |task|
386
+ running = true if task.state == 'running'
387
+ acquire_lock = true if task.state == 'acquire_lock'
388
+ successful = false unless task.state == 'finished' || task.state == 'successful'
389
+ new = false unless task.state == 'new' || task.state == 'new_popped' || task.state == 'build'
390
+ pending = true if (task.state == 'new' || task.state == 'new_popped' || task.state == 'build' || task.state == 'pending')
391
+ error = true if task.state == 'error'
392
+ init_queue = true if task.state == 'init_queue'
393
+ end
394
+
395
+ {:running => running,
396
+ :acquire_lock => acquire_lock,
397
+ :successful => successful,
398
+ :new => new,
399
+ :pending => pending,
400
+ :error => error,
401
+ :empty => task_array.empty?,
402
+ :init_queu => init_queue}
403
+ end
404
+
405
+
406
+ # Get Lock
407
+ def get_lock_for task
408
+ end
409
+
410
+
411
+ # Release Lock
412
+ def release_lock_for task
413
+ end
414
+
415
+
416
+ # Clean up locks after an error occured
417
+ def cleanup_locks_after_error_for task
418
+ release_lock_for task
419
+ Lock.release_for_task task
420
+ end
421
+
422
+
423
+ # Here you can add clean up tasks which will be executed before the auto-reload at the end of the task-queue execution. This is handy
424
+ # if you want to remove the virtual-flag from objects for example. Return true, when a config reload is needed.
425
+ def cleanup_before_auto_reload
426
+ true
427
+ end
428
+
429
+
430
+ # Initialize
431
+ def init
432
+ end
433
+
434
+
435
+ # Deinitialize
436
+ def deinit
437
+ end
438
+
439
+
440
+ # Reload config
441
+ def reload_config(last_task, args = {})
442
+ #log :msg => "#{name}: Reloading config...", :print_log => args[:print_log]
443
+ end
444
+
445
+ end
446
+ end
447
+ end
@@ -0,0 +1,4 @@
1
+ module QueueDispatcher
2
+ class DeserializationError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_support/basic_object'
2
+
3
+ module QueueDispatcher
4
+ class QueueDispatcherProxy < ActiveSupport::BasicObject
5
+ def initialize(target, options = {})
6
+ @target = target
7
+ @options = options
8
+ end
9
+
10
+ def method_missing(method, *args)
11
+ # Find or create the task_queue
12
+ terminate_immediately = @options.delete(:terminate_immediately)
13
+ terminate_immediately = terminate_immediately.nil? ? false : terminate_immediately
14
+ terminate_immediately = @options[:queue].nil? ? true : terminate_immediately
15
+ task_queue_name = @options.delete(:queue) || "#{@target.to_s}_#{::Time.now.to_f}"
16
+ task_queue = ::TaskQueue.find_or_create_by_name task_queue_name, terminate_immediately: terminate_immediately
17
+
18
+ # Create Task
19
+ default_values = {priority: 100}
20
+ mandatory_values = {target: @target, method_name: method, args: args, state: 'new', task_queue: task_queue}
21
+ ::Task.create default_values.merge(@options).merge(mandatory_values)
22
+ end
23
+ end
24
+
25
+
26
+ module MessageSending
27
+ def enqueue(options = {})
28
+ QueueDispatcherProxy.new(self, options)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,127 @@
1
+ if defined?(ActiveRecord)
2
+ class ActiveRecord::Base
3
+ # serialize to YAML
4
+ def encode_with(coder)
5
+ coder["attributes"] = @attributes
6
+ coder.tag = ['!ruby/ActiveRecord', self.class.name].join(':')
7
+ end
8
+ end
9
+ end
10
+
11
+ module Psych
12
+ module Visitors
13
+ class YAMLTree
14
+ def visit_Class(klass)
15
+ @emitter.scalar klass.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED
16
+ end
17
+ end
18
+
19
+ class ToRuby
20
+ def visit_Psych_Nodes_Scalar(o)
21
+ @st[o.anchor] = o.value if o.anchor
22
+
23
+ if klass = Psych.load_tags[o.tag]
24
+ instance = klass.allocate
25
+
26
+ if instance.respond_to?(:init_with)
27
+ coder = Psych::Coder.new(o.tag)
28
+ coder.scalar = o.value
29
+ instance.init_with coder
30
+ end
31
+
32
+ return instance
33
+ end
34
+
35
+ return o.value if o.quoted
36
+ return @ss.tokenize(o.value) unless o.tag
37
+
38
+ case o.tag
39
+ when '!binary', 'tag:yaml.org,2002:binary'
40
+ o.value.unpack('m').first
41
+ when '!str', 'tag:yaml.org,2002:str'
42
+ o.value
43
+ when "!ruby/object:DateTime"
44
+ require 'date'
45
+ @ss.parse_time(o.value).to_datetime
46
+ when "!ruby/object:Complex"
47
+ Complex(o.value)
48
+ when "!ruby/object:Rational"
49
+ Rational(o.value)
50
+ when "!ruby/class", "!ruby/module"
51
+ resolve_class o.value
52
+ when "tag:yaml.org,2002:float", "!float"
53
+ Float(@ss.tokenize(o.value))
54
+ when "!ruby/regexp"
55
+ o.value =~ /^\/(.*)\/([mixn]*)$/
56
+ source = $1
57
+ options = 0
58
+ lang = nil
59
+ ($2 || '').split('').each do |option|
60
+ case option
61
+ when 'x' then options |= Regexp::EXTENDED
62
+ when 'i' then options |= Regexp::IGNORECASE
63
+ when 'm' then options |= Regexp::MULTILINE
64
+ when 'n' then options |= Regexp::NOENCODING
65
+ else lang = option
66
+ end
67
+ end
68
+ Regexp.new(*[source, options, lang].compact)
69
+ when "!ruby/range"
70
+ args = o.value.split(/([.]{2,3})/, 2).map { |s|
71
+ accept Nodes::Scalar.new(s)
72
+ }
73
+ args.push(args.delete_at(1) == '...')
74
+ Range.new(*args)
75
+ when /^!ruby\/sym(bol)?:?(.*)?$/
76
+ o.value.to_sym
77
+ else
78
+ @ss.tokenize o.value
79
+ end
80
+ end
81
+
82
+ def visit_Psych_Nodes_Mapping_with_class(object)
83
+ return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
84
+
85
+ case object.tag
86
+ when /^!ruby\/ActiveRecord:(.+)$/
87
+ klass = resolve_class($1)
88
+ payload = Hash[*object.children.map { |c| accept c }]
89
+ id = payload["attributes"][klass.primary_key]
90
+ begin
91
+ if ActiveRecord::VERSION::MAJOR == 3
92
+ klass.unscoped.find(id)
93
+ else # Rails 2
94
+ klass.with_exclusive_scope { klass.find(id) }
95
+ end
96
+ rescue ActiveRecord::RecordNotFound
97
+ # raise QueueDispatcher::DeserializationError
98
+
99
+ # Return ActiveRecord without reload
100
+ revive(klass, object)
101
+ end
102
+ when /^!ruby\/Mongoid:(.+)$/
103
+ klass = resolve_class($1)
104
+ payload = Hash[*object.children.map { |c| accept c }]
105
+ begin
106
+ klass.find(payload["attributes"]["_id"])
107
+ rescue Mongoid::Errors::DocumentNotFound
108
+ # raise QueueDispatcher::DeserializationError
109
+
110
+ # Return ActiveRecord without reload
111
+ revive(klass, object)
112
+ end
113
+ else
114
+ visit_Psych_Nodes_Mapping_without_class(object)
115
+ end
116
+ end
117
+ alias_method_chain :visit_Psych_Nodes_Mapping, :class
118
+
119
+ def resolve_class_with_constantize(klass_name)
120
+ klass_name.constantize
121
+ rescue
122
+ resolve_class_without_constantize(klass_name)
123
+ end
124
+ alias_method_chain :resolve_class, :constantize
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,15 @@
1
+ module QdLogger
2
+ attr_accessor :logger
3
+
4
+ def initialize_logger(logger = nil)
5
+ @logger = logger || Logger.new("#{File.expand_path(Rails.root)}/log/queue_dispatcher.log")
6
+ end
7
+
8
+ # Write a standart log message
9
+ def log(args = {})
10
+ sev = args[:sev] || :info
11
+ msg = Time.now.to_s + " #{sev.to_s.upcase} #{$$} (#{self.class.name}): " + args[:msg]
12
+ logger.send(sev, msg) if logger
13
+ puts "#{sev.to_s.upcase}: #{args[:msg]}" if logger.nil? || args[:print_log]
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ module QueueDispatcher
2
+ class RcAndMsg
3
+ attr_accessor :rc, :output, :error_msg
4
+
5
+
6
+ def self.good_rc(output, args = {})
7
+ rc_and_msg = new
8
+ rc_and_msg.good_rc(output, args)
9
+ end
10
+
11
+
12
+ def self.bad_rc(error_msg, args = {})
13
+ rc_and_msg = new
14
+ rc_and_msg.bad_rc(error_msg, args)
15
+ end
16
+
17
+
18
+ # Initializer
19
+ def initialize(args = {})
20
+ @rc = args[:rc].to_i if args[:rc]
21
+ @output = args[:output]
22
+ @error_msg = args[:error_msg]
23
+ self
24
+ end
25
+
26
+
27
+ # Fake a good RC
28
+ def good_rc(output, args = {})
29
+ @rc = 0
30
+ @output = output
31
+ @error_msg = args[:error_msg]
32
+ self
33
+ end
34
+
35
+
36
+ # Fake a bad RC
37
+ def bad_rc(error_msg, args = {})
38
+ @rc = 999
39
+ @output = args[:output]
40
+ @error_msg = error_msg
41
+ self
42
+ end
43
+
44
+
45
+ # Addition
46
+ def +(other)
47
+ rc_and_msg = self.clone
48
+ rc_and_msg.rc += other.rc
49
+ rc_and_msg.output += "\n#{other.output}" if other.output.present?
50
+ rc_and_msg.error_msg += "\n#{other.error_msg}" if other.error_msg.present?
51
+ rc_and_msg
52
+ end
53
+
54
+
55
+ # Return hash
56
+ def to_hash
57
+ { :rc => @rc, :output => @output, :error_msg => @error_msg }
58
+ end
59
+ end
60
+ end