Commit b6eabacb30256369e13b122362ac584a3a45ab21
1 parent
83f48698
Exists in
master
and in
29 other branches
ActionItem96: implementing task system
git-svn-id: https://svn.colivre.coop.br/svn/noosfero/trunk@611 3f533792-8f58-4932-b0fe-aaf55b0a4547
Showing
3 changed files
with
134 additions
and
2 deletions
Show diff stats
app/models/task.rb
1 | +# Task is the base class of ... tasks! Its instances represents tasks that must | ||
2 | +# be confirmed by someone (like an environment administrator) or by noosfero | ||
3 | +# itself. | ||
4 | +# | ||
5 | +# The specific types of tasks <em>must</em> override the #perform method, so | ||
6 | +# the actual action associated to the type of task can be performed. See the | ||
7 | +# documentation of the #perform method for details. | ||
8 | +# | ||
9 | +# This class has a +status+ field of type <tt>text</tt>, where you can store | ||
10 | +# any type of data (as serialized Ruby objects) you need for your subclass . | ||
1 | class Task < ActiveRecord::Base | 11 | class Task < ActiveRecord::Base |
12 | + | ||
13 | + module Status | ||
14 | + # the status of tasks just created | ||
15 | + ACTIVE = 1 | ||
16 | + | ||
17 | + # the status of a task that was cancelled. | ||
18 | + CANCELLED = 2 | ||
19 | + | ||
20 | + # the status os a task that was successfully finished | ||
21 | + FINISHED = 3 | ||
22 | + end | ||
23 | + | ||
2 | belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id | 24 | belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id |
3 | belongs_to :target, :class_name => 'Profile', :foreign_key => :target_id | 25 | belongs_to :target, :class_name => 'Profile', :foreign_key => :target_id |
26 | + | ||
27 | + def initialize(*args) | ||
28 | + super | ||
29 | + self.status ||= Task::Status::ACTIVE | ||
30 | + end | ||
31 | + | ||
32 | + # this method finished the task. It calls #perform, which must be overriden | ||
33 | + # by subclasses. At the end a message (as returned by #finish_message) is | ||
34 | + # sent to the requestor with #notify_requestor. | ||
35 | + def finish | ||
36 | + transaction do | ||
37 | + self.status = Task::Status::FINISHED | ||
38 | + self.end_date = Time.now | ||
39 | + self.save! | ||
40 | + self.perform | ||
41 | + self.notify_requestor(self.finish_message) | ||
42 | + end | ||
43 | + end | ||
44 | + | ||
45 | + # this method cancels the task. At the end a message (as returned by | ||
46 | + # #cancel_message) is sent to the requestor with #notify_requestor. | ||
47 | + def cancel | ||
48 | + transaction do | ||
49 | + self.status = Task::Status::CANCELLED | ||
50 | + self.end_date = Time.now | ||
51 | + self.save! | ||
52 | + self.notify_requestor(self.cancel_message) | ||
53 | + end | ||
54 | + end | ||
55 | + | ||
56 | + protected | ||
57 | + | ||
58 | + # This method must be overrided in subclasses, and its implementation must do | ||
59 | + # the job the task is intended to. This method will be called when the finish | ||
60 | + # method is called. | ||
61 | + # | ||
62 | + # To cancel the finish of the task, you can throw an exception in perform. | ||
63 | + # | ||
64 | + # The implementation on Task class just does nothing. | ||
65 | + def perform | ||
66 | + end | ||
67 | + | ||
68 | + # sends a message to the requestor | ||
69 | + def notify_requestor(msg) | ||
70 | + # TODO: implement message sending | ||
71 | + end | ||
72 | + | ||
73 | + # The message that will be sent to the requestor of the task when its | ||
74 | + # finished. | ||
75 | + def finish_message | ||
76 | + _("The task was finished at %s") % (self.end_date.to_s) | ||
77 | + end | ||
78 | + | ||
79 | + # The message that will be sent to the requestor of the task when its | ||
80 | + # cancelled. | ||
81 | + def cancel_message | ||
82 | + _("The task was cancelled at %s") % (self.end_date.to_s) | ||
83 | + end | ||
84 | + | ||
4 | end | 85 | end |
db/migrate/017_create_tasks.rb
1 | class CreateTasks < ActiveRecord::Migration | 1 | class CreateTasks < ActiveRecord::Migration |
2 | def self.up | 2 | def self.up |
3 | create_table :tasks do |t| | 3 | create_table :tasks do |t| |
4 | + t.column :description, :string | ||
4 | 5 | ||
5 | t.column :data, :text | 6 | t.column :data, :text |
6 | t.column :status, :integer | 7 | t.column :status, :integer |
8 | + t.column :end_date, :date | ||
7 | 9 | ||
8 | t.column :requestor_id, :integer | 10 | t.column :requestor_id, :integer |
9 | t.column :target_id, :integer | 11 | t.column :target_id, :integer |
test/unit/task_test.rb
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../test_helper' | @@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../test_helper' | ||
3 | class TaskTest < Test::Unit::TestCase | 3 | class TaskTest < Test::Unit::TestCase |
4 | 4 | ||
5 | def test_relationship_with_requestor | 5 | def test_relationship_with_requestor |
6 | - t = Task.new | 6 | + t = Task.create |
7 | assert_raise ActiveRecord::AssociationTypeMismatch do | 7 | assert_raise ActiveRecord::AssociationTypeMismatch do |
8 | t.requestor = 1 | 8 | t.requestor = 1 |
9 | end | 9 | end |
@@ -13,7 +13,7 @@ class TaskTest < Test::Unit::TestCase | @@ -13,7 +13,7 @@ class TaskTest < Test::Unit::TestCase | ||
13 | end | 13 | end |
14 | 14 | ||
15 | def test_relationship_with_target | 15 | def test_relationship_with_target |
16 | - t = Task.new | 16 | + t = Task.create |
17 | assert_raise ActiveRecord::AssociationTypeMismatch do | 17 | assert_raise ActiveRecord::AssociationTypeMismatch do |
18 | t.target = 1 | 18 | t.target = 1 |
19 | end | 19 | end |
@@ -21,4 +21,53 @@ class TaskTest < Test::Unit::TestCase | @@ -21,4 +21,53 @@ class TaskTest < Test::Unit::TestCase | ||
21 | t.target = Profile.new | 21 | t.target = Profile.new |
22 | end | 22 | end |
23 | end | 23 | end |
24 | + | ||
25 | + def test_should_call_perform_in_finish | ||
26 | + t = Task.create | ||
27 | + t.expects(:perform) | ||
28 | + t.finish | ||
29 | + assert_equal Task::Status::FINISHED, t.status | ||
30 | + end | ||
31 | + | ||
32 | + def test_should_have_cancelled_status_after_cancel | ||
33 | + t = Task.create | ||
34 | + t.cancel | ||
35 | + assert_equal Task::Status::CANCELLED, t.status | ||
36 | + end | ||
37 | + | ||
38 | + def test_should_start_with_active_status | ||
39 | + t = Task.create | ||
40 | + assert_equal Task::Status::ACTIVE, t.status | ||
41 | + end | ||
42 | + | ||
43 | + def test_should_notify_finish | ||
44 | + t = Task.create | ||
45 | + t.expects(:notify_requestor) | ||
46 | + t.expects(:finish_message) | ||
47 | + t.finish | ||
48 | + end | ||
49 | + | ||
50 | + def test_should_notify_cancel | ||
51 | + t = Task.create | ||
52 | + t.expects(:notify_requestor) | ||
53 | + t.expects(:cancel_message) | ||
54 | + t.cancel | ||
55 | + end | ||
56 | + | ||
57 | + def test_should_not_notify_when_perform_fails | ||
58 | + count = Task.count | ||
59 | + | ||
60 | + t = Task.create | ||
61 | + class << t | ||
62 | + def perform | ||
63 | + raise RuntimeError | ||
64 | + end | ||
65 | + end | ||
66 | + | ||
67 | + t.expects(:notify_requestor).never | ||
68 | + assert_raise RuntimeError do | ||
69 | + t.finish | ||
70 | + end | ||
71 | + end | ||
72 | + | ||
24 | end | 73 | end |