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


Reply via email to