workflow_core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6e37fe908ba22a89b01f43680721ad466544bb1eef47cc8bc95ea26d7a77c00d
4
+ data.tar.gz: 1a052885996e219ec88a60d1e890e7b7b49b855b30bef3b073d1b3a98cc19167
5
+ SHA512:
6
+ metadata.gz: ad2e77a5043dcbd124a489ef4e0cb047fe3ef15de5db8d23eab6aea2c0f331299cefedb19f9100378db82555cc094ec535ad7d86383b4d50dcb21e5f7b6baada
7
+ data.tar.gz: af92427c841e043165b8ad2c7179f83bb1edd4906ebef9adcd54eddaedbde9669b695f8ecfbe486399b846b03b2da5898360ff6be6b82c290e8a0caa23a86ce7
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Jun Jiang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+ Workflow Core
2
+ ====
3
+
4
+ > WorkflowCore is under development, the codebase is unoptimized and has many bad practices, I may do breaking changes even force pushing to master branch.
5
+ >
6
+ > In short, it's not ready yet, but I realize that a workflow engine is complicated to design and it needs a long term to done well, so I decide to open source at early stage.
7
+ >
8
+ > Any way, feedbacks and suggestions are highly welcome!
9
+
10
+ A Rails engine which providing essential infrastructure of workflow.
11
+
12
+ It's based on [Workflow Net](http://mlwiki.org/index.php/Workflow_Nets) technique.
13
+
14
+ The gem provides:
15
+
16
+ - Models to describe workflow nets
17
+ - Models to describe workflow instances
18
+ - Interfaces to define transitions
19
+
20
+ ## Why “core”
21
+
22
+ Because it's not aim to "out-of-box", some gem like Devise giving developer an out-of-box experience, that's awesome, but on the other hand, it also introducing a very complex abstraction that may hard to understanding how it works, especially when you attempting to customize it.
23
+
24
+ I believe that the gem is tightly coupled with features that face to end users directly, so having a good customizability and easy to understanding are of the most concern, so I just wanna give you a domain framework that you can build your own that just fitting your need, and you shall have fully control and without any unnecessary abstraction.
25
+
26
+ BTW, the dummy app is a full-featured app with production level codebase that you can freely to reference it.
27
+
28
+ ## Todo
29
+
30
+ - Make sure transit must be an atomic operation, and help developers to avoiding Rails' nested transaction pitfalls.
31
+ - Consider consequences of changing the Net, well handle Net changes. e.g: what about running instances?
32
+ - Stabilizing interfaces.
33
+ - Evaluate that can supporting async, scheduled and event-based transition properly.
34
+ - Efficiency (especially on database).
35
+ - Easy to use.
36
+ - Transforming to graph representation for visualization and other usages (e.g proving [Soundness](http://mlwiki.org/index.php/Workflow_Soundness)).
37
+ - Polish codebase.
38
+ - Continually improving dummy app.
39
+
40
+ ## Requirements
41
+
42
+ - MRI 2.3+
43
+ - Rails 5.0+
44
+
45
+ ## Usage
46
+
47
+ See demo for now.
48
+
49
+ ## Installation
50
+
51
+ Add this line to your Gemfile:
52
+
53
+ ```ruby
54
+ gem 'workflow_core'
55
+ ```
56
+
57
+ Or you may want to include the gem directly from GitHub:
58
+
59
+ ```ruby
60
+ gem 'workflow_core', github: 'rails-engine/workflow_core'
61
+ ```
62
+
63
+ And then execute:
64
+
65
+ ```sh
66
+ $ bundle
67
+ ```
68
+
69
+ Copy migrations
70
+
71
+ ```sh
72
+ $ bin/rails workflow_core:install:migrations
73
+ ```
74
+
75
+ Then do migrate
76
+
77
+ ```sh
78
+ $ bin/rails db:migrate
79
+ ```
80
+
81
+ ## Demo
82
+
83
+ **Demo is also under development.**
84
+
85
+ The dummy app integrates with [Form Core](https://github.com/rails-engine/form_core) and [Script Core](https://github.com/rails-engine/script_core) shows an Approving Manage System.
86
+
87
+ ![](_assets/dummy_overview.png)
88
+
89
+ ### Features
90
+
91
+ #### Importing workflow definitions from a BPMN2 xml,
92
+
93
+ ![](_assets/importing_bpmn.png)
94
+
95
+ Because there isn't have a easy-to-use web-based flowchart designer, I implement a stupid BPMN2 importer, it have many restrictions:
96
+
97
+ - Only supports `Sequence`, `Start event`, `End event`, `Parallel gateway` and `Exclusive gateway`
98
+ - Using gateway to fork flows must have corresponding join (or merge) gateway
99
+ - Only read `name` property, other such as `condition expression` must configure on the dummy app
100
+
101
+ You can check `_samples` folder, or you can try a BPMN2 designer (e.g [Camunda modeler](https://github.com/camunda/camunda-modeler)).
102
+
103
+ #### Defining form
104
+
105
+ ![](_assets/defining_form.png)
106
+
107
+ You can defining a dynamic form for a workflow.
108
+
109
+ In transition's options, you can configure field's accessibility
110
+
111
+ #### Exclusive choice configuration supports Ruby expression
112
+
113
+ ![](_assets/editing_transition.png)
114
+
115
+ Exclusive choice is a special transition that needs to configure conditions that determine how to transit to a branch.
116
+
117
+ The condition is a Ruby expression, and running in a mRuby sandbox (powered by ScriptCore but it's also undone yet), and you can access form data through `@input[:payload]`, for example, there is a field named `approved`, we can check the field checked by `@input[:payload]["approved"]`
118
+
119
+ #### Run a workflow
120
+
121
+ That should make sense.
122
+
123
+ ### Usage
124
+
125
+ Clone the repository.
126
+
127
+ ```sh
128
+ $ git clone https://github.com/rails-engine/workflow_core.git
129
+ ```
130
+
131
+ Change directory
132
+
133
+ ```sh
134
+ $ cd workflow_core
135
+ ```
136
+
137
+ Run bundler
138
+
139
+ ```sh
140
+ $ bundle install
141
+ ```
142
+
143
+ Preparing database
144
+
145
+ ```sh
146
+ $ bin/rails db:migrate
147
+ ```
148
+
149
+ Start the Rails server
150
+
151
+ ```sh
152
+ $ bin/rails s
153
+ ```
154
+
155
+ Open your browser, and visit `http://localhost:3000`
156
+
157
+ ## Contributing
158
+
159
+ Bug report or pull request are welcome.
160
+
161
+ ### Make a pull request
162
+
163
+ 1. Fork it
164
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
165
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
166
+ 4. Push to the branch (`git push origin my-new-feature`)
167
+ 5. Create new Pull Request
168
+
169
+ Please write unit test with your code if necessary.
170
+
171
+ ## License
172
+
173
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "bundler/setup"
5
+ rescue LoadError
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
+ end
8
+
9
+ require "rdoc/task"
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = "rdoc"
13
+ rdoc.title = "WorkflowCore"
14
+ rdoc.options << "--line-numbers"
15
+ rdoc.rdoc_files.include("README.md")
16
+ rdoc.rdoc_files.include("lib/**/*.rb")
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
20
+ load "rails/tasks/engine.rake"
21
+
22
+ load "rails/tasks/statistics.rake"
23
+
24
+ require "bundler/gem_tasks"
25
+
26
+ require "rake/testtask"
27
+
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << "test"
30
+ t.pattern = "test/**/*_test.rb"
31
+ t.verbose = false
32
+ end
33
+
34
+ task default: :test
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class Place < ApplicationRecord
5
+ self.table_name = "workflow_places"
6
+
7
+ belongs_to :workflow
8
+
9
+ belongs_to :input_transition, optional: true, foreign_key: "input_transition_id",
10
+ class_name: "WorkflowCore::Transition"
11
+ belongs_to :output_transition, optional: true, foreign_key: "output_transition_id",
12
+ class_name: "WorkflowCore::Transition"
13
+
14
+ has_many :tokens
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class Token < ApplicationRecord
5
+ self.table_name = "workflow_tokens"
6
+
7
+ belongs_to :instance,
8
+ class_name: "WorkflowCore::WorkflowInstance"
9
+ belongs_to :workflow
10
+
11
+ belongs_to :place
12
+ belongs_to :previous, optional: true,
13
+ class_name: "WorkflowCore::Token"
14
+
15
+ enum status: {
16
+ processing: 0,
17
+ completed: 1,
18
+ failed: 2,
19
+ unexpected: 3,
20
+ terminated: 4
21
+ }
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class Transition < ApplicationRecord
5
+ self.table_name = "workflow_transitions"
6
+
7
+ belongs_to :workflow
8
+
9
+ has_many :input_places, dependent: :nullify,
10
+ foreign_key: "output_transition_id", class_name: "WorkflowCore::Place"
11
+ has_many :output_places, dependent: :destroy,
12
+ foreign_key: "input_transition_id", class_name: "WorkflowCore::Place"
13
+
14
+ def fire(_token)
15
+ raise NotImplementedError
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class Workflow < ApplicationRecord
5
+ self.table_name = "workflows"
6
+
7
+ has_one :start_place, class_name: "WorkflowCore::Place", dependent: :destroy
8
+
9
+ has_many :transitions, class_name: "WorkflowCore::Transition", dependent: :destroy
10
+ has_many :places, class_name: "WorkflowCore::Place", dependent: :destroy
11
+
12
+ has_many :instances, class_name: "WorkflowCore::WorkflowInstance", dependent: :destroy
13
+ has_many :tokens, class_name: "WorkflowCore::Token", dependent: :destroy
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class WorkflowInstance < ApplicationRecord
5
+ self.table_name = "workflow_instances"
6
+
7
+ belongs_to :workflow
8
+ has_many :tokens, foreign_key: "instance_id", dependent: :destroy
9
+
10
+ enum status: {
11
+ processing: 0,
12
+ completed: 1,
13
+ failed: 2,
14
+ unexpected: 3,
15
+ terminated: 4
16
+ }
17
+
18
+ serialize :payload
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkflows < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :workflows do |t|
6
+ t.string :type, null: false
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkflowPlaces < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :workflow_places do |t|
6
+ t.references :input_transition, foreign_key: {to_table: "workflow_transitions"}
7
+ t.references :output_transition, foreign_key: {to_table: "workflow_transitions"}
8
+
9
+ t.string :type, null: false
10
+ t.references :workflow, foreign_key: true
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkflowTransitions < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :workflow_transitions do |t|
6
+ t.string :type, null: false
7
+ t.references :workflow, foreign_key: true
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkflowInstances < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :workflow_instances do |t|
6
+ t.text :payload
7
+ t.integer :status, null: false, default: 0
8
+
9
+ t.references :workflow, foreign_key: true
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWorkflowTokens < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :workflow_tokens do |t|
6
+ t.integer :status, null: false, default: 0
7
+
8
+ t.references :place, foreign_key: {to_table: "workflow_places"}
9
+ t.references :previous, foreign_key: {to_table: "workflow_tokens"}
10
+
11
+ t.references :instance, foreign_key: {to_table: "workflow_instances"}
12
+ t.references :workflow, foreign_key: true
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # desc "Explaining what the task does"
4
+ # task :workflow_core do
5
+ # # Task goes here
6
+ # end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace WorkflowCore
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorkflowCore
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "workflow_core/engine"
4
+
5
+ module WorkflowCore
6
+ # Your code goes here...
7
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: workflow_core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - jasl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.2'
27
+ description: A Rails engine which providing essential infrastructure of workflow.
28
+ It's based on Workflow Nets.
29
+ email:
30
+ - jasl9187@hotmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - MIT-LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - app/jobs/workflow_core/application_job.rb
39
+ - app/models/workflow_core/application_record.rb
40
+ - app/models/workflow_core/place.rb
41
+ - app/models/workflow_core/token.rb
42
+ - app/models/workflow_core/transition.rb
43
+ - app/models/workflow_core/workflow.rb
44
+ - app/models/workflow_core/workflow_instance.rb
45
+ - db/migrate/20180910195950_create_workflows.rb
46
+ - db/migrate/20180910200203_create_workflow_places.rb
47
+ - db/migrate/20180910200454_create_workflow_transitions.rb
48
+ - db/migrate/20180912223642_create_workflow_instances.rb
49
+ - db/migrate/20180912223722_create_workflow_tokens.rb
50
+ - lib/tasks/workflow_core_tasks.rake
51
+ - lib/workflow_core.rb
52
+ - lib/workflow_core/engine.rb
53
+ - lib/workflow_core/version.rb
54
+ homepage: https://github.com/rails-engine/workflow_core
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.7.7
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: A Rails engine which providing essential infrastructure of workflow. It's
78
+ based on Workflow Nets.
79
+ test_files: []