stonepath 0.0.3 → 0.0.4

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
@@ -1,3 +1,12 @@
1
+ # This is a concept that existed in the older Journeyman workflow engine, but I'm not sure
2
+ # that StonePath needs it. It is proving more worthwhile to ust rely on any number of other
3
+ # active_record models for providing group functionality, and you can see this id pretty much
4
+ # an empty stub.
5
+
6
+ # Groups were another aggregation of work much like users were. With the general concept of
7
+ # WorkBench, I think this will be leaving the framework soon.
8
+ # -db!
9
+
1
10
  module StonePath
2
11
  module Group
3
12
  def self.included(base)
@@ -1,3 +1,7 @@
1
+ # This is a concept that existed in the older Journeyman workflow engine, but I'm not sure
2
+ # that StonePath needs it. It is proving more worthwhile to ust rely on any number of other
3
+ # gems for providing role functionality, and you can see this id pretty much an empty stub.
4
+
1
5
  module StonePath
2
6
  module Role
3
7
  def self.included(base)
@@ -1,11 +1,21 @@
1
+ # A Task in this framework is simply a relation between a workitem and a workbench. It has
2
+ # some default workflow, and should be extended with whatever attributes make sense for the
3
+ # business domain you are modeling.
4
+
1
5
  module StonePath
2
6
  module SPTask
3
7
 
8
+ # This will move from here shortly, into another class/module for containing things like this.
9
+ # This is the workflow definition for a default task. This is defined this way so that users
10
+ # can provide their own task workflow definition as a block to the stonepath_task declaration.
11
+ # This lanbda is passed in if the user doesn't provide anything.
12
+ # It is possible that we will identify other useful options and want to provide them as config
13
+ # blocks in the StonePath gem.
4
14
  def self.default_config_block
5
15
  lambda {
6
16
  aasm_initial_state :active
7
17
  aasm_state :active, :after_enter => :notify_created
8
- aasm_state :completed, :before_enter => :timestamp_complete, :after_enter => :notify_closed
18
+ aasm_state :completed, :after_enter => [:timestamp_complete, :notify_closed]
9
19
  aasm_state :expired, :after_enter => :notify_closed
10
20
  aasm_state :cancelled, :after_enter => :notify_closed
11
21
 
@@ -31,16 +41,20 @@ module StonePath
31
41
  }
32
42
  end
33
43
 
44
+
34
45
  def self.included(base)
35
46
  base.instance_eval do
36
47
 
37
- belongs_to :assignee, :polymorphic => true
48
+ # Tasks are now completely polymorphic between workbenches.
49
+ # as long as an activerecord model declares itself as a workbench and declares itself
50
+ # a workbench for the specific kind of task, everything just works.
51
+ belongs_to :workbench, :polymorphic => true
52
+
53
+ # Tasks are now completely polymorphic between workitems.
54
+ # as long as an activerecord model declares itself as a workitem and declares itself
55
+ # a workitem for the specific kind of task, everything just works.
56
+ belongs_to :workitem, :polymorphic => true
38
57
 
39
- def task_for(workitem, options={})
40
- options.merge!(:class_name => workitem.to_s.classify)
41
- belongs_to :workitem, options
42
- end
43
-
44
58
  def audits_transitions
45
59
  puts "#{self.class} audits transitions"
46
60
  end
@@ -68,7 +82,7 @@ module StonePath
68
82
  end
69
83
 
70
84
  def notify_closed
71
- if workitem.respond_to?(:task_closed)
85
+ if workitem.respond_to?(:task_closed)
72
86
  workitem.task_closed(self)
73
87
  end
74
88
  end
@@ -5,7 +5,7 @@ module StonePath
5
5
  def workbench_for(tasks, options={})
6
6
  #options.merge!(:foreign_key => :assignee_id)
7
7
  #puts options
8
- has_many tasks, :as => :assignee
8
+ has_many tasks, :as => :workbench
9
9
  end
10
10
  end
11
11
  end
@@ -1,3 +1,7 @@
1
+ # The WorkItem is the center of this framework. It is the thing that has a workflow,
2
+ # is the subject of ownership and tasks. Tis is the place the primaey state machine will
3
+ # exist
4
+
1
5
  module StonePath
2
6
  module WorkItem
3
7
  def self.included(base)
@@ -9,15 +13,12 @@ module StonePath
9
13
  belongs_to :owner, options
10
14
  end
11
15
 
12
- def subject_of(tasks, options={})
13
- has_many tasks, options
16
+ def tasked_through(tasks, options={})
17
+ has_many tasks, :as => :workitem
14
18
  end
15
19
 
16
20
  def stonepath_acl()
17
21
  require File.expand_path(File.dirname(__FILE__)) + "/acl.rb"
18
- #require File.expand_path(File.dirname(__FILE__)) + "/acl/acl.rb"
19
- #require File.expand_path(File.dirname(__FILE__)) + "/acl/acl_role.rb"
20
- #require File.expand_path(File.dirname(__FILE__)) + "/acl/acl_state.rb"
21
22
  cattr_accessor :acl
22
23
  self.acl = StonePath::ACL::Controller.new(self)
23
24
  yield self.acl
@@ -31,23 +32,32 @@ module StonePath
31
32
  class << self
32
33
  alias_method "define_attribute_methods_without_hook", "define_attribute_methods"
33
34
  alias_method "define_attribute_methods", "define_attribute_methods_with_hook"
35
+ end
36
+ end #base.instance_eval
37
+
38
+ def allowed?(method)
39
+ acl.allowed?(aasm_current_state, current_user, method)
40
+ end
41
+
42
+ # modifies to_xml do that it includes all the possible events from this state.
43
+ # useful when you are using WorkItems as resources with ActiveResource
44
+ def to_xml_with_events
45
+ to_xml_without_events do |xml|
46
+ xml.aasm_events_for_current_state(:type=>"array") do
47
+ aasm_events_for_current_state.each do |e|
48
+ xml.aasm_event do
49
+ xml.name e.to_s
50
+ end
51
+ end
52
+ end
34
53
  end
35
54
  end
36
55
 
37
- def allowed?(method)
38
- acl.allowed?(aasm_current_state, current_user, method)
56
+ base.instance_eval do
57
+ alias_method_chain :to_xml, :events
39
58
  end
40
59
 
41
- end
60
+ end #self.included
61
+
42
62
  end
43
- end
44
-
45
-
46
- # if table_exists? <workitem>_transition_log_entries
47
- #define WorkItem::TransitionLogEntry
48
- # then for each transition method, have an after proc that
49
- # creates workitem_transition_log
50
- # workitem_id
51
- # transitioned_to
52
- # transitioned_by
53
- # transitioned_at
63
+ end
@@ -1,3 +1,10 @@
1
+ # every WorkItem has one and exactly one owner. In some domains, WorkOwners and WorkBenches will
2
+ # be the same thing, but in other domains they are separate concepts. the owner is 'responsible'
3
+ # for the WorkItem in a larger sense - but the WorkBenches are 'responsible' for the completion of
4
+ # tasks associated with the WorkItem.
5
+ #
6
+ # This separation allows workflows where the owner assigns out work, and may oe may not be
7
+ # responsible for the actual completion of the work.
1
8
  module StonePath
2
9
  module WorkOwner
3
10
  def self.included(base)
data/stonepath.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stonepath}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["David Bock"]
@@ -42,6 +42,7 @@ Gem::Specification.new do |s|
42
42
  "script/destroy",
43
43
  "script/generate",
44
44
  "stonepath.gemspec",
45
+ "stonepath.pdf",
45
46
  "test/acl_test.rb",
46
47
  "test/app_root/app/controllers/application_controller.rb",
47
48
  "test/app_root/app/models/assignment.rb",
data/stonepath.pdf ADDED
Binary file
@@ -3,7 +3,7 @@ class Assignment < ActiveRecord::Base
3
3
 
4
4
  stonepath_task
5
5
 
6
- task_for :case
6
+ #task_for :case
7
7
 
8
8
  audits_transitions
9
9
 
@@ -18,7 +18,7 @@ class Case < ActiveRecord::Base
18
18
  end
19
19
 
20
20
  owned_by :user
21
- subject_of :assignments
21
+ tasked_through :assignments
22
22
 
23
23
 
24
24
 
@@ -63,5 +63,17 @@ class Case < ActiveRecord::Base
63
63
  end
64
64
  end
65
65
 
66
+
67
+ def task_created(task)
68
+ self.notification_method = "created"
69
+ self.notified_id = task.id
70
+ self.save
71
+ end
66
72
 
73
+ def task_closed(task)
74
+ self.notification_method = "closed"
75
+ self.notified_id = task.id
76
+ self.save
77
+ end
78
+
67
79
  end
@@ -17,6 +17,5 @@ class CustomAssignment < ActiveRecord::Base
17
17
  end
18
18
  end
19
19
 
20
- task_for :case
21
20
 
22
21
  end
@@ -3,8 +3,12 @@ class CreateAssignments < ActiveRecord::Migration
3
3
  def self.up
4
4
  create_table :assignments do |t|
5
5
  t.string :aasm_state
6
- t.integer :case_id
6
+ t.integer :workitem_id
7
+ t.string :workitem_type
8
+ t.integer :workbench_id
9
+ t.string :workbench_type
7
10
  t.datetime :completed_at
11
+ t.datetime :due_at
8
12
  t.timestamps
9
13
  end
10
14
  end
@@ -5,6 +5,8 @@ class CreateCases < ActiveRecord::Migration
5
5
  t.string :name
6
6
  t.string :regarding
7
7
  t.string :aasm_state
8
+ t.string :notification_method
9
+ t.integer :notified_id
8
10
  t.timestamps
9
11
  end
10
12
  end
data/test/task_test.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
2
 
3
+ require 'flexmock/test_unit'
4
+
3
5
  class TaskTest < Test::Unit::TestCase
4
6
 
5
7
  def setup
@@ -38,6 +40,51 @@ class TaskTest < Test::Unit::TestCase
38
40
  assert_equal("expired", a.aasm_state)
39
41
  end
40
42
 
43
+ should "be overdue? if due_at is in the past" do
44
+ c = Case.create
45
+ a = c.assignments.create(:due_at => 1.week.ago)
46
+ assert(a.overdue?)
47
+ end
48
+
49
+ should "be able to set up the relationship between a case and an assignment" do
50
+ c = Case.create
51
+ a = c.assignments.create
52
+ assert_equal(a, c.assignments[0])
53
+ assert_equal(c, c.assignments[0].workitem)
54
+ end
55
+
56
+ should "be able to set up the relationship between a user (as a workbench) and an assignment" do
57
+ u = User.create
58
+ c = Case.create
59
+ a = c.assignments.create(:workbench => u)
60
+
61
+ assert_equal(a, u.assignments[0])
62
+ assert_equal(u, a.workbench)
63
+ end
64
+
65
+ # This whole event notification thing is ugly.
66
+ # Putting aside the ugly way I tested this, the whole AASM callback mechanism
67
+ # has a problem that the id isn't set after the callback from a create.
68
+ # Looking at the code of aasm, I cannot easily figure out why.
69
+ should "callback the workitem when a task is created" do
70
+ c = Case.create
71
+ cid = c.id
72
+ a = c.assignments.create
73
+ c = Case.find cid
74
+ assert_equal("created", c.notification_method)
75
+ #assert_equal(a.id, c.notified_id)
76
+ end
77
+
78
+ should "callback the workitem when a task is completed" do
79
+ c = Case.create
80
+ cid = c.id
81
+ a = c.assignments.create
82
+ a.complete!
83
+ c = Case.find cid
84
+ assert_equal("closed", c.notification_method)
85
+ #assert_equal(a.id, c.notified_id)
86
+ end
87
+
41
88
  end
42
89
 
43
90
 
@@ -8,4 +8,10 @@ class WorkitemTest < Test::Unit::TestCase
8
8
  should "not do much of anything yet" do
9
9
  c = Case.new
10
10
  end
11
+
12
+ should "contain xml for the possible events" do
13
+ c = Case.create
14
+ xml = c.to_xml
15
+ assert(xml.include?("<aasm_events_for_current_state type=\"array\">"))
16
+ end
11
17
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stonepath
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Bock
@@ -57,6 +57,7 @@ files:
57
57
  - script/destroy
58
58
  - script/generate
59
59
  - stonepath.gemspec
60
+ - stonepath.pdf
60
61
  - test/acl_test.rb
61
62
  - test/app_root/app/controllers/application_controller.rb
62
63
  - test/app_root/app/models/assignment.rb