rails_workflow 0.4.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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