petri_flow 0.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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/config/wf_manifest.js +2 -0
  6. data/app/assets/javascripts/wf/application.js +4 -0
  7. data/app/assets/stylesheets/wf/application.scss +2 -0
  8. data/app/assets/stylesheets/wf/arcs.css +4 -0
  9. data/app/assets/stylesheets/wf/cases.css +4 -0
  10. data/app/assets/stylesheets/wf/comments.css +4 -0
  11. data/app/assets/stylesheets/wf/fields.css +4 -0
  12. data/app/assets/stylesheets/wf/forms.css +4 -0
  13. data/app/assets/stylesheets/wf/guards.css +4 -0
  14. data/app/assets/stylesheets/wf/places.css +4 -0
  15. data/app/assets/stylesheets/wf/transitions.css +4 -0
  16. data/app/assets/stylesheets/wf/workflows.css +4 -0
  17. data/app/assets/stylesheets/wf/workitem_assignments.css +4 -0
  18. data/app/assets/stylesheets/wf/workitems.css +4 -0
  19. data/app/controllers/wf/application_controller.rb +14 -0
  20. data/app/controllers/wf/arcs_controller.rb +61 -0
  21. data/app/controllers/wf/cases_controller.rb +48 -0
  22. data/app/controllers/wf/comments_controller.rb +30 -0
  23. data/app/controllers/wf/fields_controller.rb +54 -0
  24. data/app/controllers/wf/forms_controller.rb +59 -0
  25. data/app/controllers/wf/guards_controller.rb +58 -0
  26. data/app/controllers/wf/places_controller.rb +53 -0
  27. data/app/controllers/wf/transitions_controller.rb +69 -0
  28. data/app/controllers/wf/workflows_controller.rb +59 -0
  29. data/app/controllers/wf/workitem_assignments_controller.rb +30 -0
  30. data/app/controllers/wf/workitems_controller.rb +79 -0
  31. data/app/helpers/wf/application_helper.rb +6 -0
  32. data/app/helpers/wf/arcs_helper.rb +6 -0
  33. data/app/helpers/wf/cases_helper.rb +6 -0
  34. data/app/helpers/wf/comments_helper.rb +6 -0
  35. data/app/helpers/wf/fields_helper.rb +6 -0
  36. data/app/helpers/wf/forms_helper.rb +6 -0
  37. data/app/helpers/wf/guards_helper.rb +6 -0
  38. data/app/helpers/wf/places_helper.rb +6 -0
  39. data/app/helpers/wf/transitions_helper.rb +6 -0
  40. data/app/helpers/wf/workflows_helper.rb +6 -0
  41. data/app/helpers/wf/workitem_assignments_helper.rb +6 -0
  42. data/app/helpers/wf/workitems_helper.rb +6 -0
  43. data/app/jobs/wf/application_job.rb +6 -0
  44. data/app/jobs/wf/fire_timed_workitem_job.rb +15 -0
  45. data/app/mailers/wf/application_mailer.rb +8 -0
  46. data/app/models/wf/application_record.rb +7 -0
  47. data/app/models/wf/arc.rb +42 -0
  48. data/app/models/wf/callbacks/assignment_default.rb +12 -0
  49. data/app/models/wf/callbacks/deadline_default.rb +11 -0
  50. data/app/models/wf/callbacks/enable_default.rb +11 -0
  51. data/app/models/wf/callbacks/fire_default.rb +11 -0
  52. data/app/models/wf/callbacks/hold_timeout_default.rb +11 -0
  53. data/app/models/wf/callbacks/notification_default.rb +11 -0
  54. data/app/models/wf/callbacks/time_default.rb +11 -0
  55. data/app/models/wf/callbacks/unassignment_default.rb +11 -0
  56. data/app/models/wf/case.rb +44 -0
  57. data/app/models/wf/case_assignment.rb +22 -0
  58. data/app/models/wf/case_command/add_comment.rb +17 -0
  59. data/app/models/wf/case_command/add_manual_assignment.rb +17 -0
  60. data/app/models/wf/case_command/add_token.rb +20 -0
  61. data/app/models/wf/case_command/add_workitem_assignment.rb +35 -0
  62. data/app/models/wf/case_command/begin_workitem_action.rb +33 -0
  63. data/app/models/wf/case_command/cancel.rb +17 -0
  64. data/app/models/wf/case_command/cancel_workitem.rb +20 -0
  65. data/app/models/wf/case_command/clear_manual_assignments.rb +16 -0
  66. data/app/models/wf/case_command/clear_workitem_assignments.rb +20 -0
  67. data/app/models/wf/case_command/consume_token.rb +23 -0
  68. data/app/models/wf/case_command/enable_transitions.rb +33 -0
  69. data/app/models/wf/case_command/end_workitem_action.rb +25 -0
  70. data/app/models/wf/case_command/finish_workitem.rb +18 -0
  71. data/app/models/wf/case_command/finished_p.rb +28 -0
  72. data/app/models/wf/case_command/fire_message_transition.rb +20 -0
  73. data/app/models/wf/case_command/fire_transition_internal.rb +41 -0
  74. data/app/models/wf/case_command/lock_token.rb +21 -0
  75. data/app/models/wf/case_command/new.rb +17 -0
  76. data/app/models/wf/case_command/release_token.rb +20 -0
  77. data/app/models/wf/case_command/remove_manual_assignment.rb +17 -0
  78. data/app/models/wf/case_command/remove_workitem_assignment.rb +24 -0
  79. data/app/models/wf/case_command/resume.rb +17 -0
  80. data/app/models/wf/case_command/set_workitem_assignments.rb +32 -0
  81. data/app/models/wf/case_command/start_case.rb +19 -0
  82. data/app/models/wf/case_command/start_workitem.rb +26 -0
  83. data/app/models/wf/case_command/suspend.rb +17 -0
  84. data/app/models/wf/case_command/sweep_automatic_transitions.rb +31 -0
  85. data/app/models/wf/case_command/sweep_timed_transitions.rb +16 -0
  86. data/app/models/wf/case_command/workitem_action.rb +20 -0
  87. data/app/models/wf/comment.rb +21 -0
  88. data/app/models/wf/demo_target.rb +18 -0
  89. data/app/models/wf/entry.rb +33 -0
  90. data/app/models/wf/field.rb +53 -0
  91. data/app/models/wf/field_value.rb +30 -0
  92. data/app/models/wf/form.rb +18 -0
  93. data/app/models/wf/group.rb +21 -0
  94. data/app/models/wf/guard.rb +73 -0
  95. data/app/models/wf/party.rb +22 -0
  96. data/app/models/wf/place.rb +28 -0
  97. data/app/models/wf/token.rb +37 -0
  98. data/app/models/wf/transition.rb +49 -0
  99. data/app/models/wf/transition_static_assignment.rb +21 -0
  100. data/app/models/wf/user.rb +26 -0
  101. data/app/models/wf/workflow.rb +182 -0
  102. data/app/models/wf/workitem.rb +78 -0
  103. data/app/models/wf/workitem_assignment.rb +19 -0
  104. data/app/views/layouts/wf/_alert.html.erb +4 -0
  105. data/app/views/layouts/wf/_footer.html.erb +13 -0
  106. data/app/views/layouts/wf/_nav.html.erb +13 -0
  107. data/app/views/layouts/wf/_notice.html.erb +4 -0
  108. data/app/views/layouts/wf/application.html.erb +34 -0
  109. data/app/views/wf/arcs/_form.html.erb +40 -0
  110. data/app/views/wf/arcs/edit.html.erb +1 -0
  111. data/app/views/wf/arcs/new.html.erb +1 -0
  112. data/app/views/wf/arcs/show.html.erb +67 -0
  113. data/app/views/wf/cases/_form.html.erb +27 -0
  114. data/app/views/wf/cases/index.html.erb +35 -0
  115. data/app/views/wf/cases/new.html.erb +1 -0
  116. data/app/views/wf/cases/show.html.erb +113 -0
  117. data/app/views/wf/comments/new.html.erb +27 -0
  118. data/app/views/wf/fields/_form.html.erb +42 -0
  119. data/app/views/wf/fields/edit.html.erb +1 -0
  120. data/app/views/wf/fields/new.html.erb +1 -0
  121. data/app/views/wf/forms/_form.html.erb +32 -0
  122. data/app/views/wf/forms/edit.html.erb +1 -0
  123. data/app/views/wf/forms/index.html.erb +32 -0
  124. data/app/views/wf/forms/new.html.erb +1 -0
  125. data/app/views/wf/forms/show.html.erb +53 -0
  126. data/app/views/wf/guards/_form.html.erb +43 -0
  127. data/app/views/wf/guards/edit.html.erb +1 -0
  128. data/app/views/wf/guards/new.html.erb +1 -0
  129. data/app/views/wf/places/_form.html.erb +43 -0
  130. data/app/views/wf/places/edit.html.erb +1 -0
  131. data/app/views/wf/places/new.html.erb +1 -0
  132. data/app/views/wf/transitions/_form.html.erb +92 -0
  133. data/app/views/wf/transitions/edit.html.erb +1 -0
  134. data/app/views/wf/transitions/new.html.erb +1 -0
  135. data/app/views/wf/workflows/_form.html.erb +32 -0
  136. data/app/views/wf/workflows/edit.html.erb +1 -0
  137. data/app/views/wf/workflows/index.html.erb +38 -0
  138. data/app/views/wf/workflows/new.html.erb +1 -0
  139. data/app/views/wf/workflows/show.html.erb +177 -0
  140. data/app/views/wf/workitem_assignments/new.html.erb +27 -0
  141. data/app/views/wf/workitems/index.html.erb +68 -0
  142. data/app/views/wf/workitems/pre_finish.html.erb +33 -0
  143. data/app/views/wf/workitems/show.html.erb +211 -0
  144. data/config/routes.rb +30 -0
  145. data/db/migrate/20200130201043_init.rb +226 -0
  146. data/db/migrate/20200130201641_init_some_data.rb +15 -0
  147. data/db/migrate/20200131200455_create_wf_entries.rb +19 -0
  148. data/db/migrate/20200201001543_add_target_field_name_for_guard.rb +7 -0
  149. data/lib/tasks/wf_tasks.rake +5 -0
  150. data/lib/wf/engine.rb +10 -0
  151. data/lib/wf/version.rb +5 -0
  152. data/lib/wf.rb +7 -0
  153. metadata +312 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class CancelWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ raise("The workitem is not in state #{workitem.state}") unless workitem.started?
13
+ Wf::ApplicationRecord.transaction do
14
+ workitem.update!(state: :canceled, :canceled_at: Time.zone.now)
15
+ ReleaseToken.call(workitem)
16
+ SweepAutomaticTransitions.call(workitem.case)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ClearManualAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :transition
7
+ def initialize(wf_case, transition)
8
+ @wf_case = wf_case
9
+ @transition = transition
10
+ end
11
+
12
+ def call
13
+ wf_case.case_assignments.where(transition: transition).find_each(&:destroy)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ClearWorkitemAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :permanent
7
+ def initialize(workitem, permanent = true)
8
+ @workitem = workitem
9
+ @permanent = permanent
10
+ end
11
+
12
+ def call
13
+ Wf::ApplicationRecord.transaction do
14
+ ClearManualAssignments.call(workitem.case, workitem.transition) if permanent
15
+ workitem.workitem_assignments.delete_all
16
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ConsumeToken
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :place, :locked_item
7
+ def initialize(wf_case, place, locked_item = nil)
8
+ @wf_case = wf_case
9
+ @place = place
10
+ @locked_item = locked_item
11
+ end
12
+
13
+ def call
14
+ Wf::ApplicationRecord.transaction do
15
+ if locked_item
16
+ wf_case.tokens.where(place: place, state: :locked, locked_workitem_id: locked_item.id).update(consumed_at: Time.zone.now, state: :consumed)
17
+ else
18
+ wf_case.tokens.where(id: wf_case.tokens.where(place: place, state: :free).first&.id).update(consumed_at: Time.zone.now, state: :consumed)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class EnableTransitions
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ wf_case.workitems.enabled.each do |workitem|
14
+ workitem.update!(state: :overridden, overridden_at: Time.zone.now) unless wf_case.can_fire?(workitem.transition)
15
+ end
16
+ wf_case.workflow.transitions.each do |transition|
17
+ next unless wf_case.can_fire?(transition) && !transition.workitems.where(case: wf_case, state: %i[enabled started]).exists?
18
+
19
+ trigger_time = Time.zone.now + transition.trigger_limit.minutes if transition.trigger_limit && transition.time?
20
+ workitem = wf_case.workitems.create!(
21
+ workflow: wf_case.workflow,
22
+ transition: transition,
23
+ state: :enabled,
24
+ trigger_time: trigger_time
25
+ )
26
+ Wf::FireTimedWorkitemJob.set(wait: transition.trigger_limit.minutes).perform_later(workitem.id) if trigger_time
27
+ SetWorkitemAssignments.call(workitem)
28
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class EndWorkitemAction
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :action, :user
7
+ def initialize(workitem, user, action = :start)
8
+ @workitem = workitem
9
+ @action = action
10
+ @user = user
11
+ end
12
+
13
+ def call
14
+ if action == :start
15
+ StartWorkitem.call(workitem, user)
16
+ elsif action == :finish
17
+ FinishWorkitem.call(workitem, user)
18
+ elsif action == :cancel
19
+ CancelWorkitem.call(workitem, user)
20
+ elsif action == :comment
21
+ raise("Unknown action #{action}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FinishWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ FireTransitionInternal.call(workitem)
14
+ SweepAutomaticTransitions.call(workitem.case)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FinishedP
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ return true if wf_case.finished?
13
+
14
+ end_place = wf_case.workflow.places.end.first
15
+ end_place_token_num = Wf::ApplicationRecord.uncached { wf_case.tokens.where(place: end_place).count }
16
+ if end_place_token_num == 0
17
+ false
18
+ else
19
+ free_and_locked_token_num = wf_case.tokens.where(place: end_place).where(state: %i[free locked]).count
20
+ raise("The workflow net is misconstructed: Some parallel executions have not finished.") if free_and_locked_token_num > 1
21
+
22
+ ConsumeToken.call(wf_case, end_place)
23
+ wf_case.finished!
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FireMessageTransition
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ raise("Transition #{workitem.transition.name} is not message triggered") unless workitem.transition.message?
13
+
14
+ Wf::ApplicationRecord.transaction do
15
+ FireTransitionInternal.call(workitem)
16
+ SweepAutomaticTransitions.call(workitem.case)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class FireTransitionInternal
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ if workitem.enabled?
13
+ locked_item = nil
14
+ elsif workitem.started?
15
+ locked_item = workitem
16
+ else
17
+ raise("can not fire the transition if it is not in state enabled or started.")
18
+ end
19
+ Wf::ApplicationRecord.transaction do
20
+ workitem.update!(finished_at: Time.zone.now, state: :finished)
21
+ # TODO: only in?
22
+ workitem.transition.arcs.each do |arc|
23
+ ConsumeToken.call(workitem.case, arc.place, locked_item)
24
+ end
25
+ # last arc without guard -> pass
26
+ has_passed = false
27
+ workitem.transition.arcs.out.order("guards_count DESC").each do |arc|
28
+ if workitem.transition.explicit_or_split?
29
+ if workitem.pass_guard?(arc, has_passed)
30
+ has_passed = true
31
+ AddToken.call(workitem.case, arc.place)
32
+ end
33
+ else
34
+ AddToken.call(workitem.case, arc.place)
35
+ end
36
+ end
37
+ workitem.transition.fire_callback.constantize.new(workitem.id).perform_now
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class LockToken
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :place, :workitem
7
+ def initialize(wf_case, place, workitem)
8
+ @wf_case = wf_case
9
+ @place = place
10
+ @workitem = workitem
11
+ end
12
+
13
+ def call
14
+ wf_case.tokens.free.where(place: place).limit(1).update_all(
15
+ state: :locked,
16
+ locked_at: Time.zone.now,
17
+ locked_workitem_id: workitem.id
18
+ )
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class New
5
+ prepend SimpleCommand
6
+ attr_reader :workflow, :target
7
+ def initialize(workflow, target = nil)
8
+ @workflow = workflow
9
+ @target = target
10
+ end
11
+
12
+ def call
13
+ wf_case = workflow.cases.create!(targetable: target, state: :created)
14
+ wf_case
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class ReleaseToken
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ Wf::Token.where(locked_workitem_id: workitem.id).locked.each do |token|
14
+ AddToken.call(token.case, token.place)
15
+ token.update!(state: :canceled, canceled_at: Time.zone.now)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class RemoveManualAssignment
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case, :transition, :party
7
+ def initialize(wf_case, transition, party)
8
+ @wf_case = wf_case
9
+ @transition = transition
10
+ @party = party
11
+ end
12
+
13
+ def call
14
+ wf_case.case_assignments.where(transition: transition, party: party).find_each(&:destroy)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class RemoveWorkitemAssignment
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :party, :permanent
7
+ def initialize(workitem, party, permanent = true)
8
+ @workitem = workitem
9
+ @party = party
10
+ @permanent = permanent
11
+ end
12
+
13
+ def call
14
+ return if party.nil?
15
+
16
+ Wf::ApplicationRecord.transaction do
17
+ RemoveManualAssignment.call(workitem.case, workitem.transition, party) if permanent
18
+ workitem.workitem_assignments.where(party: party).first&.destroy
19
+
20
+ workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class Resume
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ raise("Only suspended or canceled cases can be resumed") unless wf_case.suspended? || wf_case.canceled?
13
+
14
+ wf_case.active!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SetWorkitemAssignments
5
+ prepend SimpleCommand
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ has_case_ass = false
14
+ workitem.case.case_assignments.where(transition: workitem.transition).find_each do |case_ass|
15
+ AddWorkitemAssignment.call(workitem, case_ass.party, false)
16
+ has_case_ass = true
17
+ end
18
+
19
+ unless has_case_ass
20
+ callback_values = workitem.transition.assignment_callback.constantize.new(workitem.id).perform
21
+ if callback_values.present?
22
+ # TODO: do assignment for callback.
23
+ else
24
+ workitem.transition.transition_static_assignments.each do |static_assignment|
25
+ AddWorkitemAssignment.call(workitem, static_assignment.party, false)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class StartCase
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ wf_case.active!
14
+ AddToken.call(wf_case, wf_case.workflow.places.start.first)
15
+ SweepAutomaticTransitions.call(wf_case)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class StartWorkitem
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :user
7
+ def initialize(workitem, user)
8
+ @workitem = workitem
9
+ @user = user
10
+ end
11
+
12
+ def call
13
+ raise("The workitem is not in state #{workitem.state}") unless workitem.enabled?
14
+
15
+ # TODO: holding timeout
16
+ Wf::ApplicationRecord.transaction do
17
+ workitem.update!(state: :started, holding_user: user)
18
+
19
+ workitem.transition.arcs.in.each do |arc|
20
+ LockToken.call(workitem.case, arc.place, workitem)
21
+ end
22
+ end
23
+ workitem
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class Suspend
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ raise("Only active or suspended cases can be canceled") unless wf_case.active?
13
+
14
+ wf_case.suspended!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SweepAutomaticTransitions
5
+ prepend SimpleCommand
6
+ attr_reader :wf_case
7
+ def initialize(wf_case)
8
+ @wf_case = wf_case
9
+ end
10
+
11
+ def call
12
+ Wf::ApplicationRecord.transaction do
13
+ EnableTransitions.call(wf_case)
14
+ done = false
15
+ until done
16
+ done = true
17
+ finished = FinishedP.call(wf_case).result
18
+ next if finished
19
+
20
+ Wf::ApplicationRecord.uncached do
21
+ wf_case.workitems.joins(:transition).where(state: :enabled).where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:automatic] }).find_each do |item|
22
+ FireTransitionInternal.call(item)
23
+ done = false
24
+ end
25
+ end
26
+ EnableTransitions.call(wf_case)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class SweepTimedTransitions
5
+ prepend SimpleCommand
6
+
7
+ def call
8
+ Wf::ApplicationRecord.transaction do
9
+ Wf::Workitem.enabled.where("trigger_time <= ?", Time.zone.now).find_each do |item|
10
+ FireTransitionInternal.call(item)
11
+ SweepAutomaticTransitions.call(item.case)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wf::CaseCommand
4
+ class WorkitemAction
5
+ prepend SimpleCommand
6
+ attr_reader :workitem, :action, :user
7
+ def initialize(workitem, user, action = :start)
8
+ @workitem = workitem
9
+ @action = action
10
+ @user = user
11
+ end
12
+
13
+ def call
14
+ Wf::ApplicationRecord.transaction do
15
+ BeginWorkitemAction.call(workitem, user, action)
16
+ EndWorkitemAction.call(workitem, user, action)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # == Schema Information
3
+ #
4
+ # Table name: wf_comments
5
+ #
6
+ # id :integer not null, primary key
7
+ # workitem_id :integer
8
+ # user_id :string
9
+ # body :text
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ #
13
+
14
+ # frozen_string_literal: true
15
+
16
+ module Wf
17
+ class Comment < ApplicationRecord
18
+ belongs_to :workitem
19
+ belongs_to :user, class_name: Wf::Workflow.user_class.to_s
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_demo_targets
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string
9
+ # description :string
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ #
13
+
14
+ module Wf
15
+ class DemoTarget < ApplicationRecord
16
+ has_many :cases, as: :targetable
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_entries
6
+ #
7
+ # id :integer not null, primary key
8
+ # user_id :string
9
+ # workitem_id :integer
10
+ # payload :json default("{}")
11
+ # created_at :datetime not null
12
+ # updated_at :datetime not null
13
+ #
14
+
15
+ module Wf
16
+ class Entry < ApplicationRecord
17
+ belongs_to :user, class_name: Wf::Workflow.user_class.to_s
18
+ belongs_to :workitem
19
+ has_many :field_values
20
+
21
+ after_initialize do
22
+ self.payload = {} if payload.blank?
23
+ end
24
+
25
+ def json
26
+ field_values.includes(:field).map { |x| [x.field_id.to_i, { field_id: x.id.to_i, field_name: x.field.name, value: x.value_after_cast }] }.to_h
27
+ end
28
+
29
+ def update_payload!
30
+ update(payload: json)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: wf_fields
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string
9
+ # form_id :integer
10
+ # position :integer default("0")
11
+ # field_type :integer default("0")
12
+ # field_type_name :string
13
+ # default_value :string
14
+ # created_at :datetime not null
15
+ # updated_at :datetime not null
16
+ #
17
+
18
+ module Wf
19
+ class Field < ApplicationRecord
20
+ belongs_to :form, touch: true
21
+
22
+ enum field_type: {
23
+ string: 0,
24
+ integer: 1,
25
+ boolean: 2,
26
+ date: 3,
27
+ datetime: 4,
28
+ decimal: 5,
29
+ float: 6,
30
+ json: 7,
31
+ text: 8,
32
+
33
+ "string[]": 20,
34
+ "integer[]": 21,
35
+ "date[]": 23,
36
+ "datetime[]": 24,
37
+ "decimal[]": 25,
38
+ "float[]": 26,
39
+ "json[]": 27,
40
+ "text[]": 28
41
+ }
42
+
43
+ def type_for_cast
44
+ type = field_type.to_s.match(/^(\w+)(\[\])?$/)[1]
45
+ is_array = field_type.to_s.match(/^(\w+)(\[\])?$/)[2] == "[]"
46
+ if is_array
47
+ ActiveRecord::Type.lookup(type.to_sym, array: true)
48
+ else
49
+ ActiveRecord::Type.lookup(type.to_sym)
50
+ end
51
+ end
52
+ end
53
+ end