queue_dispatcher 1.1.0

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