rails_workflow 0.4.4 → 0.7.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/concerns/rails_workflow/status.rb +4 -0
  3. data/app/concerns/rails_workflow/user/assignment.rb +1 -0
  4. data/app/controllers/rails_workflow/operation_templates_controller.rb +25 -29
  5. data/app/controllers/rails_workflow/operations_controller.rb +1 -1
  6. data/app/controllers/rails_workflow/process_templates_controller.rb +1 -1
  7. data/app/controllers/rails_workflow/processes_controller.rb +1 -1
  8. data/app/models/rails_workflow/event_operation.rb +15 -0
  9. data/app/models/rails_workflow/operation.rb +13 -3
  10. data/app/models/rails_workflow/operation_template.rb +14 -0
  11. data/app/models/rails_workflow/process.rb +4 -2
  12. data/app/views/rails_workflow/operation_templates/_event_form.html.slim +43 -0
  13. data/lib/rails_workflow/config.rb +3 -0
  14. data/lib/rails_workflow/error_resolver.rb +1 -1
  15. data/lib/rails_workflow/event_manager.rb +61 -0
  16. data/lib/rails_workflow/operation_builder.rb +1 -0
  17. data/lib/rails_workflow/version.rb +1 -1
  18. data/spec/dummy/log/test.log +0 -0
  19. data/spec/factories/operation_templates.rb +12 -0
  20. data/spec/lib/error_resolver_spec.rb +2 -2
  21. data/spec/lib/event_manager_spec.rb +111 -0
  22. data/spec/lib/process_builder/events_spec.rb +31 -0
  23. data/spec/lib/process_manager_spec.rb +5 -3
  24. data/spec/lib/process_runner/events_spec.rb +72 -0
  25. data/spec/support/{rails_workflow/prepare_template.rb → contexts/process_template.rb} +1 -1
  26. data/spec/support/contexts/process_template_with_events.rb +36 -0
  27. data/spec/support/rails_workflow/custom_operation_template.rb +2 -0
  28. metadata +15 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dea54167aac6920c8627f69c61a216dcc2e537e7
4
- data.tar.gz: 1d2ae941198813cf7b96cc09b6b52f89723c41bd
3
+ metadata.gz: ab9b0acda56c082f4ede75be038abc2b4e587d97
4
+ data.tar.gz: a80bd0d24eef6b2a33e345eca16403a977d1d61f
5
5
  SHA512:
6
- metadata.gz: eb6704ad409c7f714988401587bbc392d3fe93d3bbcf804a9d25d2fa7b8f8f45ebe9f2e3b941d65a3ea48740f5a47f4bca288d3b5321523b97d52a919348c3aa
7
- data.tar.gz: 73afc4682184789ade697bcfc02fbf8365af632e07e5b4f0b60dc6b6a11f457bde92b222d1d384cda247bdf963015fcb77e802a35d16cda6f94795b5e915a04f
6
+ metadata.gz: cd6d57faeb02da76cacad1e3deeda66b2006af1ccb22980e82868cf859249293f8b7ce2beef6a1b19fcad8fc3ac986efa1902ad069ed4be825c57b1aff734e72
7
+ data.tar.gz: 5ac05170290e1b2997095cc20a64216eb01b47fe4b65a661933383db1a6487a611ff1ff27d4a94099a2fbb6a373ba3450cb2555c3d6e068471d036ae4094018f
@@ -28,6 +28,10 @@ module RailsWorkflow
28
28
  ['error', ERROR]
29
29
  ].assoc(status).last
30
30
  end
31
+
32
+ def uncompleted_statuses
33
+ [NOT_STARTED, IN_PROGRESS, WAITING]
34
+ end
31
35
  end
32
36
 
33
37
  included do
@@ -6,6 +6,7 @@ module RailsWorkflow
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
+ # TODO: change Operation to UserOperation
9
10
  has_many :operations, class_name: 'RailsWorkflow::Operation', as: :assignment
10
11
  end
11
12
 
@@ -1,19 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsWorkflow
4
+ # Used in configuration UI to CRUD operation templates
4
5
  class OperationTemplatesController < ApplicationController
5
6
  layout 'rails_workflow/application'
6
7
  before_action :set_operation_template, only: %i[show edit update destroy]
7
8
  before_action :set_process_template
8
9
  respond_to :html
9
10
 
10
- before_filter do
11
+ before_action do
11
12
  @config_section_active = true
12
13
  end
13
14
 
14
15
  def index
15
- @operation_templates = OperationTemplateDecorator
16
- .decorate_collection(operation_templates_collection)
16
+ @operation_templates =
17
+ OperationTemplateDecorator
18
+ .decorate_collection(operation_templates_collection)
17
19
  end
18
20
 
19
21
  def new
@@ -22,7 +24,9 @@ module RailsWorkflow
22
24
  end
23
25
 
24
26
  def create
25
- @operation_template = @process_template.operations.create(permitted_params)
27
+ @operation_template =
28
+ @process_template.operations.create(permitted_params)
29
+
26
30
  redirect_to process_template_operation_templates_url
27
31
  end
28
32
 
@@ -38,28 +42,18 @@ module RailsWorkflow
38
42
 
39
43
  protected
40
44
 
45
+ def permitted_attributes
46
+ [
47
+ :kind, :type, :tag, :instruction, :title,
48
+ :source, :multiple, :child_process_id,
49
+ :operation_class, :role, :partial_name, :async,
50
+ :is_background, :group,
51
+ dependencies: [:id, statuses: []]
52
+ ]
53
+ end
54
+
41
55
  def permitted_params
42
- parameters =
43
- params.permit(
44
- operation_template: [
45
- :kind,
46
- :type,
47
- :tag,
48
- :instruction,
49
- :title, :source,
50
- :child_process_id,
51
- :operation_class,
52
- :role,
53
- :partial_name,
54
- :async,
55
- :is_background,
56
- :group,
57
- dependencies: [
58
- :id,
59
- statuses: []
60
- ]
61
- ]
62
- )
56
+ parameters = params.permit(operation_template: permitted_attributes)
63
57
 
64
58
  if parameters[:operation_template].present?
65
59
  parameters[:operation_template][:dependencies] =
@@ -77,17 +71,19 @@ module RailsWorkflow
77
71
  def parse_dependencies(hash)
78
72
  hash.values.each do |dep|
79
73
  dep['id'] = dep['id'].to_i
80
- dep['statuses'] = dep['statuses'].map(&:to_i) || RailsWorkflow::OperationTemplate.all_statuses
74
+ dep['statuses'] = dep['statuses'].map(&:to_i) ||
75
+ RailsWorkflow::OperationTemplate.all_statuses
81
76
  end
82
77
  end
83
78
 
84
79
  def operation_templates_collection
85
- @operation_templates = @process_template.try(:operations) || OperationTemplate
86
- .order(id: :asc)
80
+ @operation_templates = @process_template.try(:operations) ||
81
+ OperationTemplate.order(id: :asc)
87
82
  end
88
83
 
89
84
  def set_process_template
90
- @process_template = ProcessTemplate.find(params[:process_template_id]).decorate
85
+ @process_template = ProcessTemplate
86
+ .find(params[:process_template_id]).decorate
91
87
  end
92
88
 
93
89
  def set_operation_template
@@ -10,7 +10,7 @@ module RailsWorkflow
10
10
  before_action :set_operation,
11
11
  only: %i[show edit pickup continue update destroy]
12
12
 
13
- before_filter do
13
+ before_action do
14
14
  if @process.present?
15
15
  @processes_section_active = true
16
16
  else
@@ -7,7 +7,7 @@ module RailsWorkflow
7
7
 
8
8
  before_action :set_process_template, only: %i[show edit update destroy]
9
9
 
10
- before_filter do
10
+ before_action do
11
11
  @config_section_active = true
12
12
  end
13
13
 
@@ -6,7 +6,7 @@ module RailsWorkflow
6
6
  respond_to :html
7
7
  before_action :set_process, only: %i[show edit update destroy]
8
8
 
9
- before_filter do
9
+ before_action do
10
10
  @processes_section_active = true
11
11
  end
12
12
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # Event operation - waiting/listening for some event.
5
+ class EventOperation < Operation
6
+ def can_start?
7
+ false
8
+ end
9
+
10
+ # TODO: check if redundant
11
+ def can_be_assigned?(_user)
12
+ false # any user can complete operation
13
+ end
14
+ end
15
+ end
@@ -7,22 +7,32 @@ module RailsWorkflow
7
7
  class Operation < ActiveRecord::Base
8
8
  include OperationStatus
9
9
  include Operations::Dependencies
10
+ # TODO: move to UserOperation
10
11
  include Operations::Assignments
11
12
  include HasContext
12
13
 
13
14
  belongs_to :process, class_name: 'RailsWorkflow::Process'
14
15
  alias parent process
15
16
  belongs_to :template, class_name: 'RailsWorkflow::OperationTemplate'
16
- belongs_to :child_process, class_name: 'RailsWorkflow::Process', required: false
17
+ belongs_to :child_process,
18
+ class_name: 'RailsWorkflow::Process', required: false
17
19
  has_many :workflow_errors, class_name: 'RailsWorkflow::Error', as: :parent
18
20
 
19
21
  delegate :data, to: :context
20
- delegate :role, to: :template
21
- delegate :group, to: :template
22
+ delegate :role, :multiple?, :group, to: :template
22
23
  delegate :start, :complete, :skip, :cancel, to: :runner
23
24
 
24
25
  scope :with_child_process, -> { where.not(child_process: nil) }
25
26
  scope :uncompleted, -> { where(status: user_ready_statuses) }
27
+ scope :events, lambda {
28
+ joins(:template)
29
+ .where(rails_workflow_operation_templates: { kind: 'event' })
30
+ }
31
+
32
+ scope :without_events, lambda {
33
+ joins(:template)
34
+ .where.not(rails_workflow_operation_templates: { kind: 'event' })
35
+ }
26
36
 
27
37
  def instruction
28
38
  template.instruction
@@ -5,8 +5,11 @@ module RailsWorkflow
5
5
  include OperationStatus
6
6
  include RailsWorkflow::Uuid
7
7
  include OperationTemplates::Dependencies
8
+ # TODO: move to separate UserOperationTemplate
8
9
  include OperationTemplates::Assignments
9
10
 
11
+ serialize :source, JSON
12
+
10
13
  belongs_to :process_template, class_name: 'RailsWorkflow::ProcessTemplate'
11
14
  belongs_to :child_process, class_name: 'RailsWorkflow::ProcessTemplate', required: false
12
15
 
@@ -19,6 +22,17 @@ module RailsWorkflow
19
22
  OperationTemplate.other_operations(process_template_id, id)
20
23
  end
21
24
 
25
+ def multiple=(value)
26
+ self.source ||= {}
27
+ source[:multiple] = value == 'true'
28
+ end
29
+
30
+ def multiple?
31
+ (source || {})['multiple'] == true
32
+ end
33
+
34
+ alias multiple multiple?
35
+
22
36
  class << self
23
37
  def types
24
38
  RailsWorkflow.config.operation_types
@@ -9,6 +9,8 @@ module RailsWorkflow
9
9
 
10
10
  belongs_to :template, class_name: 'RailsWorkflow::ProcessTemplate'
11
11
  has_many :operations, class_name: 'RailsWorkflow::Operation'
12
+ delegate :events, to: :operations, allow_nil: true
13
+
12
14
  has_one :parent_operation,
13
15
  class_name: 'RailsWorkflow::Operation',
14
16
  foreign_key: :child_process_id
@@ -39,13 +41,13 @@ module RailsWorkflow
39
41
 
40
42
  def uncompleted?
41
43
  uncompleted_statuses.include?(status) &&
42
- uncompleted_operations.size.zero?
44
+ uncompleted_operations.reject(&:async).size.zero?
43
45
  end
44
46
 
45
47
  # Returns set or operation that not yet completed.
46
48
  # Operation complete in DONE, SKIPPED, CANCELED, etc many other statuses
47
49
  def uncompleted_operations
48
- operations.reject(&:completed?)
50
+ operations(true).reject(&:completed?)
49
51
  end
50
52
 
51
53
  def can_start?
@@ -0,0 +1,43 @@
1
+ - if @operation_template.errors.any?
2
+ #error_explanation
3
+ h2 = "#{pluralize(@operation_template.errors.count, "error")} prohibited this wf_operation_template from being saved:"
4
+ ul
5
+ - @operation_template.errors.full_messages.each do |message|
6
+ li = message
7
+
8
+ = hidden_field_tag 'operation_template[kind]', @operation_template.kind
9
+
10
+ .form-group
11
+ = f.label :title, class: "control-label col-sm-2"
12
+ .col-sm-10
13
+ = f.text_field :title, class: "form-control"
14
+
15
+
16
+ .form-group
17
+ = f.label :operation_class, class: "control-label col-sm-2"
18
+ .col-sm-10
19
+ = f.text_field :operation_class, class: "form-control", placeholder: f.object.default_class
20
+
21
+ .form-group
22
+ = f.label :type, "Template Class", class: "control-label col-sm-2"
23
+ .col-sm-10
24
+ = f.text_field :type, class: "form-control", placeholder: f.object.default_type
25
+
26
+ .form-group
27
+ = f.label :partial_name, "Context Partial", class: "control-label col-sm-2"
28
+ .col-sm-10
29
+ = f.text_field :partial_name, class: "form-control", placeholder: "Default"
30
+
31
+ .form-group
32
+ = f.label :tag, "Event Tag", class: "control-label col-sm-2"
33
+ .col-sm-10
34
+ = f.text_field :tag, class: "form-control"
35
+
36
+ .form-group
37
+ .col-sm-2
38
+ .col-sm-10
39
+ .checkbox
40
+ label
41
+ = f.check_box :multiple, {}, 'true', 'false'
42
+ strong
43
+ = 'Multiple'
@@ -17,6 +17,7 @@ require_relative './process_runner'
17
17
  require_relative './operation_runner'
18
18
  require_relative './dependency_resolver'
19
19
  require_relative './process_manager'
20
+ require_relative './event_manager'
20
21
  require 'rails_workflow/db/pg'
21
22
 
22
23
  module RailsWorkflow
@@ -133,6 +134,8 @@ module RailsWorkflow
133
134
  @default_operation_types = {
134
135
  default: { title: 'Default Operation',
135
136
  class: 'RailsWorkflow::Operation' },
137
+ event: { title: 'Event',
138
+ class: 'RailsWorkflow::EventOperation' },
136
139
  user: { title: 'User Operation',
137
140
  class: 'RailsWorkflow::UserOperation' },
138
141
  user_role: { title: 'Operation for User By Role',
@@ -38,7 +38,7 @@ module RailsWorkflow
38
38
  end
39
39
 
40
40
  def try_restart_process
41
- return unless process.present?
41
+ return if process.nil? || process.status == Status::DONE
42
42
  process.update_attribute(:status, Status::IN_PROGRESS)
43
43
 
44
44
  process.reload
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ # This manager controls events processing. Searches matching event operations
5
+ # for a given event and merges event context to matched event operations.
6
+ class EventManager
7
+ class << self
8
+ def create_event(tag, context = {})
9
+ new(tag, context).handle
10
+ end
11
+ end
12
+
13
+ def initialize(tag, context = {})
14
+ @tag = tag
15
+ @context = context
16
+ end
17
+
18
+ def handle
19
+ event_operations.each do |event_operation|
20
+ next if not_matches(event_operation)
21
+
22
+ process_multiple_event(event_operation)
23
+ event_operation.data.merge!(@context)
24
+ event_operation.complete
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def process_multiple_event(event_operation)
31
+ return unless event_operation.multiple?
32
+
33
+ new_event_operation = operation_builder.new(
34
+ event_operation.process, event_operation.template, [event_operation]
35
+ ).create_operation
36
+
37
+ new_event_operation.start
38
+ end
39
+
40
+ def not_matches(event_operation)
41
+ event_operation.respond_to?(:match) && !event_operation.match(@context)
42
+ end
43
+
44
+ def event_operations
45
+ EventOperation.where(tag: @tag, status: Status::WAITING).all
46
+ end
47
+
48
+ # TODO: refactor all operation_builder, and other configuration methods
49
+ def operation_builder
50
+ config.operation_builder
51
+ end
52
+
53
+ def operation_runner
54
+ config.operation_runner
55
+ end
56
+
57
+ def config
58
+ RailsWorkflow.config
59
+ end
60
+ end
61
+ end
@@ -52,6 +52,7 @@ module RailsWorkflow
52
52
  attributes.with_indifferent_access
53
53
  .slice(:title, :async, :is_background)
54
54
  .merge(template: template,
55
+ tag: template.tag,
55
56
  process: process,
56
57
  status: Operation::NOT_STARTED,
57
58
  manager: process.manager,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsWorkflow
4
- VERSION = '0.4.4'
4
+ VERSION = '0.7.0'
5
5
  end
Binary file
@@ -5,11 +5,23 @@ FactoryGirl.define do
5
5
  title 'Operation Template'
6
6
  kind 'default'
7
7
  type nil
8
+ async nil
8
9
  is_background true
9
10
  association :process_template, factory: :process_template
10
11
 
11
12
  factory :parent_operation_template do
12
13
  child_process { create :process_template }
13
14
  end
15
+
16
+ factory :user_operation_template do
17
+ title 'User Operation'
18
+ kind 'user'
19
+ end
20
+
21
+ factory :event do
22
+ title 'Event'
23
+ kind 'event'
24
+ tag 'event'
25
+ end
14
26
  end
15
27
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_helper'
4
- require_relative '../support/rails_workflow/prepare_template'
4
+ require_relative '../support/contexts/process_template'
5
5
 
6
6
  module RailsWorkflow
7
7
  RSpec.describe ErrorResolver do
8
- include PrepareTemplate
8
+ include_context 'process template'
9
9
 
10
10
  let(:template) { prepare_template }
11
11
  let(:process_manager) do
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require_relative './../support/contexts/process_template_with_events'
5
+
6
+ module RailsWorkflow
7
+ RSpec.describe EventManager do
8
+ include_context 'process template with events'
9
+
10
+ it 'ignores not waiting events' do
11
+ expect { described_class.create_event(:first_event, a: 'b') }
12
+ .not_to change { process.events.first.status }
13
+ .from(Status::NOT_STARTED)
14
+ end
15
+
16
+ context 'when process started' do
17
+ before { process_manager.start_process }
18
+
19
+ it 'merges event context to event operation context' do
20
+ described_class.create_event(:first_event, a: 'b')
21
+ expect(process.events.first.data).to include(a: 'b')
22
+ end
23
+
24
+ it 'merges event context to next operations' do
25
+ described_class.create_event(:first_event, a: 'b')
26
+ new_operations = dependent_operations(process, process.events.first)
27
+
28
+ new_operations.each do |new_operation|
29
+ expect(new_operation.data).to include(a: 'b')
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'multiple event operation' do
35
+ # TODO: if first event operation failed to complete - should
36
+ # not affect other event operations for same event
37
+ before do
38
+ first_event_template.first.update(source: { multiple: true })
39
+ process_manager.start_process
40
+ end
41
+
42
+ let(:first_event_template) do
43
+ template.operations.where(tag: :first_event)
44
+ end
45
+ let(:first_event_operations) { process.events.where(tag: :first_event) }
46
+
47
+ it 'creates new event operation' do
48
+ expect { described_class.create_event(:first_event, a: 'b') }
49
+ .to change { first_event_operations.count }.from(1).to(2)
50
+ end
51
+
52
+ it 'new event operation has waiting status' do
53
+ expect { described_class.create_event(:first_event, a: 'b') }
54
+ .not_to change {
55
+ first_event_operations.where(status: Status::WAITING).count
56
+ }
57
+ end
58
+
59
+ it 'should not affect next event operations context'
60
+ end
61
+
62
+ describe 'event matching' do
63
+ context 'matches event to event operation' do
64
+ let(:custom_event_operation) do
65
+ Class.new(EventOperation) do
66
+ def match(event_context)
67
+ event_context[:check] == true
68
+ end
69
+ end
70
+ end
71
+ let(:first_event) { process.events.where(tag: :first_event).first }
72
+ let(:first_event_template) do
73
+ OperationTemplate.where(tag: :first_event).first
74
+ end
75
+
76
+ before do
77
+ stub_const('CustomEventOperation', custom_event_operation)
78
+
79
+ first_event_template.update(operation_class: 'CustomEventOperation')
80
+ process_manager.start_process
81
+ end
82
+
83
+ it 'ignores not matching event' do
84
+ expect { described_class.create_event(:first_event, check: false) }
85
+ .not_to change { first_event.reload.status }
86
+ .from(Status::WAITING)
87
+
88
+ expect { described_class.create_event(:first_event, {}) }
89
+ .not_to change { first_event.reload.status }
90
+ .from(Status::WAITING)
91
+ end
92
+
93
+ it 'catches matching event' do
94
+ described_class.create_event(:first_event, check: true)
95
+
96
+ expect { described_class.create_event(:first_event, check: true) }
97
+ .not_to change { first_event.reload.status }
98
+ .from(Status::DONE)
99
+ end
100
+ end
101
+ end
102
+
103
+ def dependent_operations(process, completed_operation)
104
+ process.operations.reload.select do |operation|
105
+ operation.dependencies.any? do |dependency|
106
+ dependency['operation_id'] == completed_operation.id
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require_relative './../../support/contexts/process_template_with_events'
5
+
6
+ module RailsWorkflow
7
+ RSpec.describe ProcessBuilder do
8
+ include_context 'process template with events'
9
+
10
+ context 'independent event' do
11
+ it 'creates two operations' do
12
+ # first independent operation and first event
13
+ expect(process.operations.size).to eq 2
14
+ end
15
+
16
+ it 'creates event' do
17
+ expect(process.events.size).to eq 1
18
+ end
19
+
20
+ it do
21
+ expect(process.events.first.status)
22
+ .to eq RailsWorkflow::Status::NOT_STARTED
23
+ end
24
+
25
+ it 'creates event context' do
26
+ event = process.events.first
27
+ expect(event.data).to include(some: 'value')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_helper'
4
- require_relative '../support/rails_workflow/prepare_template'
4
+ require_relative '../support/contexts/process_template'
5
5
 
6
6
  module RailsWorkflow
7
7
  RSpec.describe ProcessManager do
8
- include PrepareTemplate
8
+ include_context 'process template'
9
9
 
10
10
  let(:template) { prepare_template }
11
11
 
@@ -46,7 +46,9 @@ module RailsWorkflow
46
46
  before :each do
47
47
  allow_any_instance_of(RailsWorkflow::ProcessManager)
48
48
  .to receive(:complete_process)
49
- allow_any_instance_of(RailsWorkflow::OperationRunner).to receive(:complete)
49
+ allow_any_instance_of(RailsWorkflow::OperationRunner)
50
+ .to receive(:complete)
51
+
50
52
  process_manager = RailsWorkflow::ProcessManager.new process
51
53
  process_manager.start_process
52
54
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require_relative './../../support/contexts/process_template_with_events'
5
+
6
+ module RailsWorkflow
7
+ RSpec.describe ProcessRunner do
8
+ include_context 'process template with events'
9
+
10
+ it 'sets not started events to waiting status' do
11
+ expect { process_manager.start_process }
12
+ .to change { process.events.first.status }
13
+ .from(Status::NOT_STARTED).to(Status::WAITING)
14
+ end
15
+
16
+ context 'for started process' do
17
+ before { process_manager.start_process }
18
+
19
+ it 'event operation catches event' do
20
+ expect { EventManager.create_event(:first_event, a: 'b') }
21
+ .to change { process.events.first.status }
22
+ .from(Status::WAITING).to(Status::DONE)
23
+ end
24
+
25
+ it 'process waits for last sync event' do
26
+ process.operations.without_events.first.complete
27
+
28
+ expect { process.operations.without_events.first.complete }
29
+ .not_to change { process.status }.from(Status::IN_PROGRESS)
30
+ end
31
+
32
+ it 'last sync event finishes process' do
33
+ process.operations.without_events.first.complete
34
+ process.operations.without_events.last.complete
35
+
36
+ EventManager.create_event(:first_event, a: 'b')
37
+
38
+ expect { EventManager.create_event(:second_event, c: 'd') }
39
+ .to change { process.reload.status }
40
+ .from(Status::IN_PROGRESS).to(Status::DONE)
41
+ end
42
+
43
+ it 'first sync event finishes process' do
44
+ process.operations.without_events.first.complete
45
+ process.operations.without_events.last.complete
46
+
47
+ EventManager.create_event(:second_event, a: 'b')
48
+
49
+ # Now first operation is only not yet finished operation
50
+ # and we finish it by sending this event
51
+ expect { EventManager.create_event(:first_event, c: 'd') }
52
+ .to change { process.reload.status }
53
+ .from(Status::IN_PROGRESS).to(Status::DONE)
54
+ end
55
+
56
+ context 'with async event' do
57
+ before do
58
+ OperationTemplate.where(tag: :second_event).first.update(async: true)
59
+ end
60
+
61
+ it 'process does not wait for async' do
62
+ process.operations.without_events.first.complete
63
+ process.operations.without_events.last.complete
64
+
65
+ expect { EventManager.create_event(:first_event, c: 'd') }
66
+ .to change { process.reload.status }
67
+ .from(Status::IN_PROGRESS).to(Status::DONE)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PrepareTemplate
3
+ RSpec.shared_context 'process template' do
4
4
  def prepare_template_operations(template)
5
5
  operation = create :operation_template, process_template: template
6
6
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsWorkflow
4
+ RSpec.shared_context 'process template with events' do
5
+ let!(:template) { prepare_template }
6
+
7
+ let(:process) { ProcessManager.create_process template.id, some: 'value' }
8
+ let(:operation_runner) { RailsWorkflow::OperationRunner }
9
+ let(:process_manager) { RailsWorkflow::ProcessManager.new process }
10
+
11
+ def prepare_template_operations(template)
12
+ operation = create :user_operation_template, process_template: template
13
+ event = create :event, tag: 'first_event', process_template: template
14
+
15
+ template_options = {
16
+ process_template: template,
17
+ dependencies: prepare_template_dependencies(operation, event)
18
+ }
19
+
20
+ # second event
21
+ create :event, template_options.merge(tag: 'second_event')
22
+ create :user_operation_template, template_options
23
+ end
24
+
25
+ def prepare_template_dependencies(operation, event)
26
+ [{ 'id' => operation.id, 'statuses' => [RailsWorkflow::Status::DONE] },
27
+ { 'id' => event.id, 'statuses' => [RailsWorkflow::Status::DONE] }]
28
+ end
29
+
30
+ def prepare_template
31
+ template = create :process_template
32
+ prepare_template_operations(template)
33
+ template
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # TODO: REMOVE
4
+
3
5
  module RailsWorkflow
4
6
  class CustomOperationTemplate < OperationTemplate
5
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Madzhuga
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-14 00:00:00.000000000 Z
11
+ date: 2017-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -219,6 +219,7 @@ files:
219
219
  - app/jobs/rails_workflow/operation_execution_job.rb
220
220
  - app/models/rails_workflow/context.rb
221
221
  - app/models/rails_workflow/error.rb
222
+ - app/models/rails_workflow/event_operation.rb
222
223
  - app/models/rails_workflow/operation.rb
223
224
  - app/models/rails_workflow/operation_template.rb
224
225
  - app/models/rails_workflow/process.rb
@@ -234,6 +235,7 @@ files:
234
235
  - app/views/application/_flash.html.slim
235
236
  - app/views/layouts/rails_workflow/application.html.slim
236
237
  - app/views/rails_workflow/operation_templates/_default_form.html.slim
238
+ - app/views/rails_workflow/operation_templates/_event_form.html.slim
237
239
  - app/views/rails_workflow/operation_templates/_user_form.html.slim
238
240
  - app/views/rails_workflow/operation_templates/_user_group_form.html.slim
239
241
  - app/views/rails_workflow/operation_templates/_user_role_form.html.slim
@@ -266,6 +268,7 @@ files:
266
268
  - lib/rails_workflow/engine.rb
267
269
  - lib/rails_workflow/error_builder.rb
268
270
  - lib/rails_workflow/error_resolver.rb
271
+ - lib/rails_workflow/event_manager.rb
269
272
  - lib/rails_workflow/operation_builder.rb
270
273
  - lib/rails_workflow/operation_runner.rb
271
274
  - lib/rails_workflow/process_builder.rb
@@ -520,9 +523,12 @@ files:
520
523
  - spec/features/process_template_spec.rb
521
524
  - spec/lib/error_builder_spec.rb
522
525
  - spec/lib/error_resolver_spec.rb
526
+ - spec/lib/event_manager_spec.rb
523
527
  - spec/lib/operation_builder_spec.rb
528
+ - spec/lib/process_builder/events_spec.rb
524
529
  - spec/lib/process_builder_spec.rb
525
530
  - spec/lib/process_manager_spec.rb
531
+ - spec/lib/process_runner/events_spec.rb
526
532
  - spec/models/rails_workflow/context_spec.rb
527
533
  - spec/models/rails_workflow/error_spec.rb
528
534
  - spec/models/rails_workflow/operation_spec.rb
@@ -533,6 +539,8 @@ files:
533
539
  - spec/serializers/process_template_serializer_spec.rb
534
540
  - spec/services/process_importer_spec.rb
535
541
  - spec/spec_helper.rb
542
+ - spec/support/contexts/process_template.rb
543
+ - spec/support/contexts/process_template_with_events.rb
536
544
  - spec/support/controller_macros.rb
537
545
  - spec/support/jsons/broken_parent_process_template.json
538
546
  - spec/support/jsons/parent_process_template.json
@@ -541,7 +549,6 @@ files:
541
549
  - spec/support/pages/operations.rb
542
550
  - spec/support/rails_workflow/custom_operation.rb
543
551
  - spec/support/rails_workflow/custom_operation_template.rb
544
- - spec/support/rails_workflow/prepare_template.rb
545
552
  - spec/support/workflow_helper.rb
546
553
  homepage: https://github.com/madzhuga/rails_workflow
547
554
  licenses:
@@ -814,11 +821,14 @@ test_files:
814
821
  - spec/dummy/app/views/leads/edit.html.slim
815
822
  - spec/dummy/app/views/layouts/application.html.erb
816
823
  - spec/services/process_importer_spec.rb
824
+ - spec/lib/process_builder/events_spec.rb
817
825
  - spec/lib/error_resolver_spec.rb
818
826
  - spec/lib/operation_builder_spec.rb
819
827
  - spec/lib/process_builder_spec.rb
828
+ - spec/lib/process_runner/events_spec.rb
820
829
  - spec/lib/process_manager_spec.rb
821
830
  - spec/lib/error_builder_spec.rb
831
+ - spec/lib/event_manager_spec.rb
822
832
  - spec/spec_helper.rb
823
833
  - spec/models/rails_workflow/operation_template_spec.rb
824
834
  - spec/models/rails_workflow/operation_spec.rb
@@ -832,9 +842,10 @@ test_files:
832
842
  - spec/support/jsons/user_operation_template.json
833
843
  - spec/support/jsons/process_template.json
834
844
  - spec/support/jsons/parent_process_template.json
835
- - spec/support/rails_workflow/prepare_template.rb
836
845
  - spec/support/rails_workflow/custom_operation_template.rb
837
846
  - spec/support/rails_workflow/custom_operation.rb
847
+ - spec/support/contexts/process_template_with_events.rb
848
+ - spec/support/contexts/process_template.rb
838
849
  - spec/support/workflow_helper.rb
839
850
  - spec/support/pages/operations.rb
840
851
  - spec/support/controller_macros.rb