Author: assaf
Date: Wed May 14 02:39:40 2008
New Revision: 656184
URL: http://svn.apache.org/viewvc?rev=656184&view=rev
Log:
Separate reserved from ready/active states.
Modified:
ode/sandbox/singleshot/app/models/task.rb
ode/sandbox/singleshot/spec/models/task_spec.rb
Modified: ode/sandbox/singleshot/app/models/task.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/task.rb?rev=656184&r1=656183&r2=656184&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/task.rb (original)
+++ ode/sandbox/singleshot/app/models/task.rb Wed May 14 02:39:40 2008
@@ -27,7 +27,7 @@
def initialize(attributes = {}) #:nodoc:
super
self.description ||= ''
- self.state = 'ready'
+ self.state = 'reserved'
self.access_key = MD5.hexdigest(OpenSSL::Random.random_bytes(128))
self.data ||= {}
end
@@ -49,21 +49,39 @@
# --- Task state ---
# A task can be in one of these states:
- # - ready -- Ready but not yet available/active, the task will not show
on anyone's list.
- # - active -- Task performed by owner.
- # - suspended -- Cannot be claimed.
+ # - reserved -- Task exists but is not yet ready or active.
+ # - ready -- Task is ready and can be claimed by owner.
+ # - active -- Task is performed by its owner.
+ # - suspended -- Task is suspended.
# - completed -- Task has completed.
- # - cancelled -- Task was cancelled before completion.
- STATES = ['ready', 'active', 'suspended', 'completed', 'cancelled']
+ # - cancelled -- Task was cancelled.
+ #
+ # A task can start in reserved state and remain there until populated with
+ # enough information to transition to the ready state. From ready state, a
+ # stakeholder can claim the task, transitioning it to the active state. The
+ # task transitions back to ready if stakeholder releases that claim.
+ #
+ # Task can transition from ready/active to suspended and back. Task can
+ # transition to completed state only from active state, and transition to
+ # cancelled state from any other state but completed. Completed and
+ # cancelled are terminal states.
+ STATES = ['reserved', 'ready', 'active', 'suspended', 'completed',
'cancelled']
# Cannot change in mass update.
attr_protected :state
validates_inclusion_of :state, :in=>STATES
+ before_validation_on_update do |task|
+ task.state = 'ready' if task.state == 'reserved'
+ end
+
before_validation do |task|
- if task.state == 'ready'
+ case task.state
+ when 'ready', 'reserved'
task.owner = task.potential_owners.first unless task.owner ||
task.potential_owners.size > 1
task.state = 'active' if task.owner
+ when 'active'
+ task.state = 'ready' unless task.owner
end
end
@@ -74,14 +92,11 @@
task.errors.add :state, 'Cannot change state of completed task.'
elsif from == 'cancelled'
task.errors.add :state, 'Cannot change state of cancelled task.'
- elsif to == 'ready'
- task.errors.add :state, 'Cannot change state back to ready.' unless
from.nil? || from == 'ready'
+ elsif to == 'reserved'
+ task.errors.add :state, 'Cannot change state to reserved.' unless
from.nil?
elsif to == 'completed'
- if from == 'active'
- task.errors.add :state, 'Only owner can complete task.' unless
task.owner
- else
- task.errors.add :state, 'Cannot change to completed from any state but
active.'
- end
+ task.errors.add :state, 'Only owner can complete task.' unless task.owner
+ task.errors.add :state, 'Cannot change to completed from any state but
active.' unless from =='active'
end
end
Modified: ode/sandbox/singleshot/spec/models/task_spec.rb
URL:
http://svn.apache.org/viewvc/ode/sandbox/singleshot/spec/models/task_spec.rb?rev=656184&r1=656183&r2=656184&view=diff
==============================================================================
--- ode/sandbox/singleshot/spec/models/task_spec.rb (original)
+++ ode/sandbox/singleshot/spec/models/task_spec.rb Wed May 14 02:39:40 2008
@@ -73,26 +73,24 @@
describe Task, 'state' do
include Specs::Tasks
+ # Returns a task in the specified state. No validation checks are made.
+ # Applies optional attributes to task at creation.
def task_in_state(state, attributes = {})
task = Task.create(default_task.merge(attributes))
Task.update_all ["state = ?", state], ["id=?", task.id]
Task.find(task.id)
end
+ # Returns true if task can transition to specified state.
def can_transition?(task, state)
task.state = state
- task.save
- end
-
- it 'should begin as ready' do
- Task.create default_task
- Task.first.state.should == 'ready'
+ task.save && task.state == state
end
it 'should not allow mass assignment' do
- Task.create default_task.merge(:state=>'active')
- Task.first.state.should == 'ready'
- lambda { Task.first.update_attributes :state=>'active' }.should_not change
{ Task.first.state }
+ task = Task.create(default_task.merge(:state=>'active'))
+ task.state.should == 'reserved'
+ lambda { task.update_attributes :state=>'active' }.should change(task,
:state).to('ready')
end
it 'should be required to save task' do
@@ -109,42 +107,64 @@
task.should have(1).error_on(:state)
end
- it 'should not transition to ready from any other state' do
- Task::STATES.each do |from|
- can_transition?(task_in_state(from), 'ready').should be(from == 'ready')
- end
+ it 'should begin as reserved' do
+ Task.create default_task
+ Task.first.state.should == 'reserved'
end
- it 'should transition to active only from ready or suspended' do
+ it 'should not transition to reserved from any other state' do
Task::STATES.each do |from|
- can_transition?(task_in_state(from), 'active').should be(['ready',
'active', 'suspended'].include?(from))
+ can_transition?(task_in_state(from), 'reserved').should be_false
end
end
+ it 'should transition to ready on first update' do
+ task = task_in_state('reserved')
+ lambda { task.save }.should change(task, :state).to('ready')
+ end
+
+ it 'should transition from reserved to active if associated with owner' do
+ task = task_in_state('reserved')
+ lambda { task.owner = person('owner') ; task.save }.should change(task,
:state).to('active')
+ end
+
+ it 'should not transition to ready from active' do
+ task = task_in_state('active', :owner=>person('owner'))
+ lambda { task.state = 'ready' ; task.save }.should_not change(task, :state)
+ end
+
it 'should transition from ready to active if owner specified' do
- Task::STATES.each do |from|
- task = task_in_state(from)
- if from == 'ready'
- lambda { task.owner = person('owner') ; task.save }.should
change(task, :state).to('active')
- lambda { task.owner = person('owner') ; task.save }.should_not
change(task, :state)
- end
- end
+ task = task_in_state('ready')
+ lambda { task.owner = person('owner') ; task.save }.should change(task,
:state).to('active')
end
- it 'should transition to active if owner picked from potential owners' do
- Task::STATES.each do |from|
- task = task_in_state(from)
- if from == 'ready'
- lambda { task.potential_owners = person('owner') ; task.save }.should
change(task, :state).to('active')
- lambda { task.potential_owners = person('owner') ; task.save
}.should_not change(task, :state)
- end
- end
+ it 'should transition to ready from active if owner removed' do
+ task = task_in_state('active', :owner=>person('owner'))
+ lambda { task.owner = nil ; task.save }.should change(task,
:state).to('ready')
end
- it 'should transition to suspended only from ready or active' do
- Task::STATES.each do |from|
- can_transition?(task_in_state(from), 'suspended').should be(['ready',
'active', 'suspended'].include?(from))
- end
+ it 'should transition from ready to suspended and back' do
+ task = task_in_state('ready')
+ can_transition?(task, 'suspended').should be_true
+ can_transition?(task, 'ready').should be_true
+ end
+
+ it 'should transition from active to suspended and back' do
+ task = task_in_state('active', :owner=>person('owner'))
+ can_transition?(task, 'suspended').should be_true
+ can_transition?(task, 'active').should be_true
+ end
+
+ it 'should transition from ready to active if has one potential owner' do
+ task = task_in_state('ready')
+ lambda { task.potential_owners = person('owner') ; task.save }.should
change(task, :state).to('active')
+ task.owner.should == person('owner')
+ end
+
+ it 'should not transition from ready to active if more than one potential
owner' do
+ task = task_in_state('ready', :potential_owners=>people('owner', 'other'))
+ task.state.should == 'ready'
+ task.owner.should be_nil
end
it 'should transition to completed only from active' do
@@ -183,12 +203,8 @@
describe Task, 'priority' do
include Specs::Tasks
- before :each do
- @task = Task.new(default_task)
- end
-
it 'should default to 1' do
- Task.new.priority.should be(1)
+ Task.new.priority.should == 1
end
it 'should allow values from 1 to 3' do
@@ -226,8 +242,8 @@
@task = Task.new(default_task)
end
- it 'should default to nil' do
- @task.due_on.should be_nil
+ it 'should not be required' do
+ Task.create(default_task.except(:due_on)).should have(:no).errors
end
it 'should accept time and return date' do