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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +55 -0
- data/Rakefile +2 -0
- data/app/assets/images/icon_acquire_lock.png +0 -0
- data/app/assets/images/icon_error.png +0 -0
- data/app/assets/images/icon_init_queue.gif +0 -0
- data/app/assets/images/icon_pending.gif +0 -0
- data/app/assets/images/icon_running.gif +0 -0
- data/app/assets/images/icon_successful.png +0 -0
- data/app/assets/images/icon_warning.gif +0 -0
- data/app/assets/images/lock.png +0 -0
- data/app/assets/javascript/tasks.js.coffee +8 -0
- data/app/helpers/tasks_helper.rb +28 -0
- data/app/models/task_dependency.rb +2 -0
- data/app/views/queue_dispatcher_views/_search_results_events.html.haml +4 -0
- data/app/views/queue_dispatcher_views/_search_results_my_events.html.haml +4 -0
- data/app/views/queue_dispatcher_views/_task_event.html.haml +24 -0
- data/app/views/queue_dispatcher_views/_task_footer.html.erb +1 -0
- data/app/views/queue_dispatcher_views/_task_header.html.erb +12 -0
- data/app/views/queue_dispatcher_views/_task_my_event.html.haml +24 -0
- data/app/views/queue_dispatcher_views/expand_event.js.rjs +1 -0
- data/app/views/queue_dispatcher_views/my_events.html.haml +7 -0
- data/app/views/queue_dispatcher_views/my_events.js.erb +6 -0
- data/app/views/queue_dispatcher_views/update_events.js.erb +15 -0
- data/lib/generators/queue_dispatcher/migration/migration_generator.rb +51 -0
- data/lib/generators/queue_dispatcher/migration/templates/task_dependencies.rb +16 -0
- data/lib/generators/queue_dispatcher/migration/templates/task_queues.rb +18 -0
- data/lib/generators/queue_dispatcher/migration/templates/tasks.rb +25 -0
- data/lib/queue_dispatcher/acts_as_task.rb +194 -0
- data/lib/queue_dispatcher/acts_as_task_controller.rb +95 -0
- data/lib/queue_dispatcher/acts_as_task_queue.rb +447 -0
- data/lib/queue_dispatcher/deserialization_error.rb +4 -0
- data/lib/queue_dispatcher/message_sending.rb +31 -0
- data/lib/queue_dispatcher/psych_ext.rb +127 -0
- data/lib/queue_dispatcher/qd_logger.rb +15 -0
- data/lib/queue_dispatcher/rc_and_msg.rb +60 -0
- data/lib/queue_dispatcher/serialization/active_record.rb +20 -0
- data/lib/queue_dispatcher/syck_ext.rb +34 -0
- data/lib/queue_dispatcher/version.rb +3 -0
- data/lib/queue_dispatcher/yaml_ext.rb +10 -0
- data/lib/queue_dispatcher.rb +20 -0
- data/lib/tasks/queue_dispatcher.rake +7 -0
- data/queue_dispatcher.gemspec +26 -0
- data/script/queue_worker_dispatcher +259 -0
- metadata +187 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011-2012 Philip Kurmann
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= QueueDispatcher for Rails3
|
2
|
+
|
3
|
+
This Rails3 Gem implements a method to perform long running methods in the background.
|
4
|
+
Background tasks will be executed by persistent workers.
|
5
|
+
|
6
|
+
== Install
|
7
|
+
|
8
|
+
Inside your Gemfile:
|
9
|
+
|
10
|
+
gem "queue_dispatcher"
|
11
|
+
|
12
|
+
and then run:
|
13
|
+
|
14
|
+
* bundle install
|
15
|
+
* rake queue_dispatcher:sync
|
16
|
+
|
17
|
+
=== Gem Dependencies
|
18
|
+
|
19
|
+
Please check if all those requirements are satisfied on your environment.
|
20
|
+
|
21
|
+
* rails >= 3.0.0
|
22
|
+
* sys-proctable >= 0.9.1
|
23
|
+
|
24
|
+
|
25
|
+
== Inside your application
|
26
|
+
|
27
|
+
To enqueue a long running task, simple call a method through enque:
|
28
|
+
E.g.:
|
29
|
+
Assume, you have a long running job:
|
30
|
+
LongRunningMailJob.send_mail
|
31
|
+
|
32
|
+
Now we'd like to execute it in the background by simply calling:
|
33
|
+
task = LongRunningMailJob.enqueue.send_mail
|
34
|
+
|
35
|
+
If you like to put the job in a queue, you can do this by execute it the following way:
|
36
|
+
task = LongRunningMailJob.enqueue(queue: 'queue_name').send_mail
|
37
|
+
|
38
|
+
Jobs inside a queue are executed serialized, not in parallel. You can define dependencies. A task is then executed only after all dependent tasks are finished. Code to add Task dependencies:
|
39
|
+
task.dependent_tasks = another_task
|
40
|
+
|
41
|
+
== Generators
|
42
|
+
|
43
|
+
* rails generate queue_dispatcher:migration
|
44
|
+
|
45
|
+
=== Database Setup
|
46
|
+
|
47
|
+
Use
|
48
|
+
|
49
|
+
rails g queue_dispatcher:migration
|
50
|
+
|
51
|
+
This will create a database migration for the models Task and TaskQueues.
|
52
|
+
|
53
|
+
== Copyright
|
54
|
+
|
55
|
+
Copyright (c) 2011 - 2012 Philip Kurmann. See LICENSE for details.
|
data/Rakefile
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,8 @@
|
|
1
|
+
jQuery ->
|
2
|
+
if jQuery('.pagination').length
|
3
|
+
jQuery(window).scroll ->
|
4
|
+
url = jQuery('.pagination .next_page').attr('href')
|
5
|
+
if url && jQuery(window).scrollTop() > jQuery(document).height() - jQuery(window).height() - 50
|
6
|
+
jQuery('.pagination').text('Fetching more events...')
|
7
|
+
jQuery.getScript(url)
|
8
|
+
jQuery(window).scroll
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TasksHelper
|
2
|
+
def icon_for_task task
|
3
|
+
icon = 'icon_warning.gif'
|
4
|
+
alt = 'Unknwon'
|
5
|
+
|
6
|
+
if task.pending?
|
7
|
+
icon = 'icon_pending.gif'
|
8
|
+
alt = 'Pending'
|
9
|
+
elsif task.init_queue?
|
10
|
+
icon = 'icon_init_queue.gif'
|
11
|
+
alt = 'Initialize Queue'
|
12
|
+
elsif task.acquire_lock?
|
13
|
+
icon = 'icon_acquire_lock.png'
|
14
|
+
alt = 'Acquire Lock'
|
15
|
+
elsif task.running?
|
16
|
+
icon = 'icon_running.gif'
|
17
|
+
alt = 'Running'
|
18
|
+
elsif task.successful?
|
19
|
+
icon = 'icon_successful.png'
|
20
|
+
alt = 'Successful'
|
21
|
+
elsif task.error?
|
22
|
+
icon = 'icon_error.png'
|
23
|
+
alt = 'Error!'
|
24
|
+
end
|
25
|
+
|
26
|
+
image_tag(icon, :alt => alt)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
- if task.error_msg.blank?
|
2
|
+
- event_class = "event"
|
3
|
+
- event_onclick = ""
|
4
|
+
- else
|
5
|
+
- event_class = "event pointer"
|
6
|
+
- event_onclick = onclick_to_remote :url => {:controller => 'tasks', :action => 'expand_event'}, :with => "'id=#{task.id}'"
|
7
|
+
|
8
|
+
- if @expanded_events.include? task.id
|
9
|
+
- event_style = ""
|
10
|
+
- else
|
11
|
+
- event_style = "display:none"
|
12
|
+
|
13
|
+
%tr{:id => "task_#{task.id}", :class => event_class, :onclick => event_onclick }
|
14
|
+
%td.qd_icon
|
15
|
+
= icon_for_task task
|
16
|
+
= "#{task.perc_finished}%" if task.running?
|
17
|
+
%td.qd_date= task.updated_at.to_s :history
|
18
|
+
%td.qd_prosa
|
19
|
+
= task.prosa
|
20
|
+
- unless task.error_msg.blank?
|
21
|
+
= image_tag 'icon_expand.gif'
|
22
|
+
%div{:id => "task_#{task.id}_details", :class => 'qd_error_msg', :style => event_style}
|
23
|
+
= task.error_msg
|
24
|
+
%br
|
@@ -0,0 +1 @@
|
|
1
|
+
</table>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
- if task.error_msg.blank?
|
2
|
+
- event_class = "event"
|
3
|
+
- event_onclick = ""
|
4
|
+
- else
|
5
|
+
- event_class = "event pointer"
|
6
|
+
- event_onclick = onclick_to_remote :url => {:action => 'expand_event'}, :with => "'id=#{task.id}'"
|
7
|
+
|
8
|
+
- if @expanded_events.include? task.id
|
9
|
+
- event_style = ""
|
10
|
+
- else
|
11
|
+
- event_style = "display:none"
|
12
|
+
|
13
|
+
%tr{:id => "task_#{task.id}", :class => event_class, :onclick => event_onclick }
|
14
|
+
%td.qd_icon
|
15
|
+
= icon_for_task task
|
16
|
+
= task.perc_finished if task.running?
|
17
|
+
%td.qd_date= task.updated_at.to_s :history
|
18
|
+
%td.qd_prosa
|
19
|
+
= task.prosa
|
20
|
+
- unless task.error_msg.blank?
|
21
|
+
= image_tag 'icon_expand.gif'
|
22
|
+
%div{:id => "task_#{task.id}_details", :class => 'qd_error_msg', :style => event_style}
|
23
|
+
= task.error_msg
|
24
|
+
%br
|
@@ -0,0 +1 @@
|
|
1
|
+
page.visual_effect :toggle_blind, "task_#{@task.id}_details", :duration => 0.1
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% @new_tasks.each do |task| %>
|
2
|
+
jQuery('#events_table').prepend('<%=j render(partial: 'queue_dispatcher_views/task_event', locals: {task: task}) %>');
|
3
|
+
jQuery('#task_<%= task.id %>').effect('highlight');
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<% @updated_tasks.each do |task| %>
|
7
|
+
jQuery('#task_<%= task.id %>').replaceWith('<%=j render(partial: 'queue_dispatcher_views/task_event', locals: {task: task}) %>');
|
8
|
+
jQuery('#task_<%= task.id %>').effect('highlight');
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% @deleted_task_ids.each do |id| %>
|
12
|
+
jQuery('#task_<%= id %>).fadeOut();
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= update_flash %>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module QueueDispatcher
|
5
|
+
class MigrationGenerator < Rails::Generators::Base
|
6
|
+
desc "The queue_dispatcher migration generator creates a database migration for the task_queue model."
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.join(File.dirname(__FILE__), 'templates')
|
9
|
+
|
10
|
+
class_option :task_queues_table_name,
|
11
|
+
:type => :string,
|
12
|
+
:desc => "Name for the TaskQueue Table",
|
13
|
+
:required => false,
|
14
|
+
:default => "task_queues"
|
15
|
+
|
16
|
+
class_option :tasks_table_name,
|
17
|
+
:type => :string,
|
18
|
+
:desc => "Name for the Task Table",
|
19
|
+
:required => false,
|
20
|
+
:default => "tasks"
|
21
|
+
|
22
|
+
class_option :task_dependencies_table_name,
|
23
|
+
:type => :string,
|
24
|
+
:desc => "Name for the Task Dependency Table",
|
25
|
+
:required => false,
|
26
|
+
:default => "task_dependencies"
|
27
|
+
|
28
|
+
def initialize(args = [], options = {}, config = {})
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
#attr_reader :lock_table_name
|
33
|
+
|
34
|
+
# Implement the required interface for Rails::Generators::Migration.
|
35
|
+
# taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
|
36
|
+
def self.next_migration_number(dirname)
|
37
|
+
if ActiveRecord::Base.timestamped_migrations
|
38
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % (current_migration_number(dirname) + 1)].max
|
39
|
+
else
|
40
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_migration_file
|
45
|
+
migration_template 'task_queues.rb', 'db/migrate/create_task_queues.rb'
|
46
|
+
migration_template 'tasks.rb', 'db/migrate/create_tasks.rb'
|
47
|
+
migration_template 'task_dependencies.rb', 'db/migrate/create_task_dependencies.rb'
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateTaskDependencies < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table "<%= options[:task_dependencies_table_name] %>" do |t|
|
4
|
+
t.integer :task_id
|
5
|
+
t.integer :dependent_task_id
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
add_index "<%= options[:task_dependencies_table_name] %>", :task_id
|
10
|
+
add_index "<%= options[:task_dependencies_table_name] %>", :dependent_task_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table "<%= options[:task_dependencies_table_name] %>"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateTaskQueues < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table "<%= options[:task_queues_table_name] %>" do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :state
|
6
|
+
t.integer :pid
|
7
|
+
t.boolean :terminate_immediately
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index "<%= options[:task_queues_table_name] %>", :name
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table "<%= options[:task_queues_table_name] %>"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CreateTasks < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table "<%= options[:tasks_table_name] %>" do |t|
|
4
|
+
t.text :target
|
5
|
+
t.string :method_name
|
6
|
+
t.text :args
|
7
|
+
t.string :state
|
8
|
+
t.integer :priority
|
9
|
+
t.string :message
|
10
|
+
t.integer :perc_finished
|
11
|
+
t.string :remark
|
12
|
+
t.text :output
|
13
|
+
t.text :error_msg
|
14
|
+
t.integer :task_queue_id
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
|
19
|
+
add_index "<%= options[:tasks_table_name] %>", :task_queue_id
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.down
|
23
|
+
drop_table "<%= options[:tasks_table_name] %>"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module QueueDispatcher
|
2
|
+
module ActsAsTask
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class Config
|
9
|
+
attr_reader :task_queue_class_name
|
10
|
+
|
11
|
+
def initialize(task_queue_class_name)
|
12
|
+
@task_queue_class_name = task_queue_class_name.to_s.underscore
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def acts_as_task args = {}
|
19
|
+
# Include number-helper for number_to_human_size method
|
20
|
+
include ActionView::Helpers::NumberHelper
|
21
|
+
|
22
|
+
include QueueDispatcher::ActsAsTask::InstanceMethods
|
23
|
+
extend QueueDispatcher::ActsAsTask::SingletonMethods
|
24
|
+
|
25
|
+
@acts_as_task_config = QueueDispatcher::ActsAsTask::Config.new(args[:task_queue_model] || :task_queue)
|
26
|
+
|
27
|
+
belongs_to acts_as_task_config.task_queue_class_name
|
28
|
+
has_many :task_dependencies, :dependent => :destroy, :foreign_key => :task_id
|
29
|
+
has_many :dependent_tasks, :through => :task_dependencies
|
30
|
+
has_many :inverse_task_dependencies, :class_name => 'TaskDependency', :foreign_key => 'dependent_task_id', :dependent => :destroy
|
31
|
+
has_many :inverse_dependent_tasks, :through => :inverse_task_dependencies, :source => :task
|
32
|
+
validates_presence_of :target, :method_name, :state
|
33
|
+
serialize :target
|
34
|
+
serialize :args
|
35
|
+
|
36
|
+
# Add dynamic associations to task_dependency and task_property according to the class name
|
37
|
+
TaskDependency.instance_eval %Q{
|
38
|
+
belongs_to :task, :class_name => '#{self.name}'
|
39
|
+
belongs_to :dependent_task, :class_name => '#{self.name}'
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
module SingletonMethods
|
46
|
+
def acts_as_task_config
|
47
|
+
@acts_as_task_config
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
module InstanceMethods
|
53
|
+
def acts_as_task_task_queue
|
54
|
+
self.send(self.class.acts_as_task_config.task_queue_class_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Add task_id to the args
|
59
|
+
def args
|
60
|
+
a = super
|
61
|
+
a[-1] = a.last.merge(task_id: self.id) if a && a.instance_of?(Array) && a.last.instance_of?(Hash)
|
62
|
+
a
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# This method updates the task state according to the return code of their corresponding command
|
67
|
+
def update_state(rc_and_msg)
|
68
|
+
rc = output = error_msg = nil
|
69
|
+
|
70
|
+
if rc_and_msg.kind_of?(QueueDispatcher::RcAndMsg)
|
71
|
+
rc = rc_and_msg.rc
|
72
|
+
output = rc_and_msg.output
|
73
|
+
error_msg = rc_and_msg.error_msg
|
74
|
+
elsif rc_and_msg.kind_of?(Hash)
|
75
|
+
rc = rc_and_msg[:rc]
|
76
|
+
output = rc_and_msg[:output]
|
77
|
+
error_msg = rc_and_msg[:error_msg]
|
78
|
+
end
|
79
|
+
|
80
|
+
if rc.nil? || rc == 0
|
81
|
+
self.update_attributes :state => "successful",
|
82
|
+
:perc_finished => 100,
|
83
|
+
:message => output
|
84
|
+
else
|
85
|
+
self.update_attributes :state => "error",
|
86
|
+
:error_msg => error_msg,
|
87
|
+
:message => output
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Update the attributes perc_finished and message according to the args
|
93
|
+
def update_message args = {}
|
94
|
+
msg = args[:msg]
|
95
|
+
perc_finished = args[:perc_finished]
|
96
|
+
|
97
|
+
self.update_attribute :message, msg if msg
|
98
|
+
self.update_attribute :perc_finished, perc_finished if perc_finished
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Is this task new?
|
103
|
+
def new?
|
104
|
+
state == 'new' || state == 'new_popped'
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Is this task pending?
|
109
|
+
def pending?
|
110
|
+
acts_as_task_task_queue && state == 'new'
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# Is this task pending?
|
115
|
+
def acquire_lock?
|
116
|
+
acts_as_task_task_queue && acts_as_task_task_queue.running? && state == 'acquire_lock'
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# Is this task running?
|
121
|
+
def running?
|
122
|
+
acts_as_task_task_queue && acts_as_task_task_queue.running? && state == 'running'
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
# Was this task finsihed successful?
|
127
|
+
def successful?
|
128
|
+
state == 'successful' || state == 'finished'
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# Had this task error(s)?
|
133
|
+
def error?
|
134
|
+
state == 'error' || state.blank?
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# Was this task aborted?
|
139
|
+
def aborted?
|
140
|
+
state == 'aborted'
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Is this task waiting until the queue is initialized?
|
145
|
+
def init_queue?
|
146
|
+
state == 'init_queue'
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# Was this task already executed?
|
151
|
+
def executed?
|
152
|
+
successful? || error? || aborted?
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# Are all dependent_tasks executed?
|
157
|
+
def dependent_tasks_executed?
|
158
|
+
state = true
|
159
|
+
dependent_tasks.each{ |dt| state = false unless dt.executed? }
|
160
|
+
state
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# Check recursive, if one or more of the tasks, which this task is dependent on had errors
|
165
|
+
def dependent_tasks_had_errors
|
166
|
+
error = false
|
167
|
+
dependent_tasks.each do |t|
|
168
|
+
error = true if t.state == 'error' || t.dependent_tasks_had_errors
|
169
|
+
end
|
170
|
+
error
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
# Placeholder. Please override it in your model.
|
175
|
+
def prosa
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# Calculate md5-Checksum
|
180
|
+
def md5
|
181
|
+
attr_str = ""
|
182
|
+
Task.attribute_names.each{ |a| attr_str += self.send(a).to_s }
|
183
|
+
Digest('MD5').digest(attr_str)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# Execute task
|
188
|
+
def execute!
|
189
|
+
target.send(method_name, *args)
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module QueueDispatcher
|
2
|
+
module ActsAsTaskController
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class Config
|
9
|
+
attr_reader :task_class_name
|
10
|
+
|
11
|
+
def initialize(task_class_name)
|
12
|
+
@task_class_name = task_class_name.to_s.underscore
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def acts_as_task_controller args = {}
|
19
|
+
include QueueDispatcher::ActsAsTaskController::InstanceMethods
|
20
|
+
extend QueueDispatcher::ActsAsTaskController::SingletonMethods
|
21
|
+
@acts_as_task_controller_config = QueueDispatcher::ActsAsTaskController::Config.new(args[:task_model] || :task)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
module SingletonMethods
|
27
|
+
def acts_as_task_controller_config
|
28
|
+
@acts_as_task_controller_config
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
module InstanceMethods
|
34
|
+
def my_events
|
35
|
+
# Remember the selected page, if the AJAX-Request wants to update the current page
|
36
|
+
page = session[:my_events_page] = params[:page] if params[:page] || ! request.xhr?
|
37
|
+
page = session[:my_events_page] if page.nil? && request.xhr?
|
38
|
+
@tasks = current_user.send(self.class.acts_as_task_controller_config.task_class_name.pluralize).order('id DESC').page(page)
|
39
|
+
|
40
|
+
# Check for updated tasks
|
41
|
+
session[:task_updates] ||= {}
|
42
|
+
task_updates = {}
|
43
|
+
@new_tasks = []
|
44
|
+
@updated_tasks = []
|
45
|
+
@deleted_task_ids = []
|
46
|
+
|
47
|
+
if @tasks
|
48
|
+
@tasks.page(1).each do |task|
|
49
|
+
task_updates[task.id] = task.updated_at
|
50
|
+
@new_tasks << task unless session[:task_updates][task.id]
|
51
|
+
@updated_tasks << task if session[:task_updates][task.id] && session[:task_updates][task.id] != task.updated_at
|
52
|
+
end
|
53
|
+
session[:task_updates].each{ |id, updated_at| @deleted_task_ids << id unless eval(self.class.acts_as_task_controller_config.task_class_name.camelize).find_by_id(id) }
|
54
|
+
session[:task_updates] = task_updates
|
55
|
+
end
|
56
|
+
|
57
|
+
if request.xhr?
|
58
|
+
# Load expanded_events from session if this is a AJAX-request
|
59
|
+
@expanded_events = session[:acts_as_task_controller_expanded_events] || []
|
60
|
+
else
|
61
|
+
# Reset expanded_events if this is a regular request
|
62
|
+
@expanded_events = session[:acts_as_task_controller_expanded_events] = []
|
63
|
+
end
|
64
|
+
|
65
|
+
respond_to do |format|
|
66
|
+
format.html { render 'queue_dispatcher_views/my_events' }
|
67
|
+
format.js do
|
68
|
+
if params[:page]
|
69
|
+
render
|
70
|
+
else
|
71
|
+
render 'queue_dispatcher_views/update_events'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def expand_event
|
79
|
+
@task = eval(self.class.acts_as_task_controller_config.task_class_name.camelize).find(params[:id])
|
80
|
+
@expanded_events = session[:acts_as_task_controller_expanded_events] || []
|
81
|
+
if @expanded_events.include? @task.id
|
82
|
+
@expanded_events -= [@task.id]
|
83
|
+
else
|
84
|
+
@expanded_events |= [@task.id]
|
85
|
+
end
|
86
|
+
session[:acts_as_task_controller_expanded_events] = @expanded_events
|
87
|
+
|
88
|
+
respond_to do |format|
|
89
|
+
format.js { render 'queue_dispatcher_views/expand_event' }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|