Showing preview only (307K chars total). Download the full file or copy to clipboard to get everything.
Repository: hooopo/petri_flow
Branch: master
Commit: a36ba91c24dc
Files: 294
Total size: 242.8 KB
Directory structure:
gitextract_22cx_jl_/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── gempush.yml
├── .gitignore
├── .rubocop.yml
├── FormSpec.md
├── Gemfile
├── Guard.md
├── LICENSE
├── MIT-LICENSE
├── README.md
├── Rakefile
├── acts_as_party.md
├── app/
│ ├── assets/
│ │ ├── config/
│ │ │ └── wf_manifest.js
│ │ ├── images/
│ │ │ └── wf/
│ │ │ └── .keep
│ │ ├── javascripts/
│ │ │ └── wf/
│ │ │ └── application.js
│ │ └── stylesheets/
│ │ └── wf/
│ │ ├── application.scss
│ │ ├── arcs.css
│ │ ├── cases.css
│ │ ├── comments.css
│ │ ├── fields.css
│ │ ├── forms.css
│ │ ├── guards.css
│ │ ├── places.css
│ │ ├── static_assignments.css
│ │ ├── transitions.css
│ │ ├── uikit/
│ │ │ ├── _colors.scss
│ │ │ ├── _variables.scss
│ │ │ ├── alert.scss
│ │ │ ├── button.scss
│ │ │ ├── card.scss
│ │ │ ├── index.scss
│ │ │ ├── navbar.scss
│ │ │ └── table.scss
│ │ ├── workflows.css
│ │ ├── workitem_assignments.css
│ │ └── workitems.css
│ ├── controllers/
│ │ └── wf/
│ │ ├── application_controller.rb
│ │ ├── arcs_controller.rb
│ │ ├── cases_controller.rb
│ │ ├── comments_controller.rb
│ │ ├── fields_controller.rb
│ │ ├── forms_controller.rb
│ │ ├── guards_controller.rb
│ │ ├── places_controller.rb
│ │ ├── static_assignments_controller.rb
│ │ ├── transitions_controller.rb
│ │ ├── workflows_controller.rb
│ │ ├── workitem_assignments_controller.rb
│ │ └── workitems_controller.rb
│ ├── helpers/
│ │ └── wf/
│ │ ├── application_helper.rb
│ │ ├── arcs_helper.rb
│ │ ├── cases_helper.rb
│ │ ├── comments_helper.rb
│ │ ├── fields_helper.rb
│ │ ├── forms_helper.rb
│ │ ├── guards_helper.rb
│ │ ├── places_helper.rb
│ │ ├── static_assignments_helper.rb
│ │ ├── transitions_helper.rb
│ │ ├── workflows_helper.rb
│ │ ├── workitem_assignments_helper.rb
│ │ └── workitems_helper.rb
│ ├── jobs/
│ │ └── wf/
│ │ ├── application_job.rb
│ │ └── fire_timed_workitem_job.rb
│ ├── mailers/
│ │ └── wf/
│ │ └── application_mailer.rb
│ ├── models/
│ │ └── wf/
│ │ ├── acts_as_party.rb
│ │ ├── application_record.rb
│ │ ├── arc.rb
│ │ ├── callbacks/
│ │ │ ├── assignment_default.rb
│ │ │ ├── deadline_default.rb
│ │ │ ├── enable_default.rb
│ │ │ ├── fire_default.rb
│ │ │ ├── hold_timeout_default.rb
│ │ │ ├── notification_default.rb
│ │ │ ├── time_default.rb
│ │ │ └── unassignment_default.rb
│ │ ├── case.rb
│ │ ├── case_assignment.rb
│ │ ├── case_command/
│ │ │ ├── add_comment.rb
│ │ │ ├── add_manual_assignment.rb
│ │ │ ├── add_token.rb
│ │ │ ├── add_workitem_assignment.rb
│ │ │ ├── begin_workitem_action.rb
│ │ │ ├── cancel.rb
│ │ │ ├── cancel_workitem.rb
│ │ │ ├── clear_manual_assignments.rb
│ │ │ ├── clear_workitem_assignments.rb
│ │ │ ├── consume_token.rb
│ │ │ ├── create_entry.rb
│ │ │ ├── enable_transitions.rb
│ │ │ ├── end_workitem_action.rb
│ │ │ ├── finish_workitem.rb
│ │ │ ├── finished_p.rb
│ │ │ ├── fire_message_transition.rb
│ │ │ ├── fire_transition_internal.rb
│ │ │ ├── lock_token.rb
│ │ │ ├── new.rb
│ │ │ ├── release_token.rb
│ │ │ ├── remove_manual_assignment.rb
│ │ │ ├── remove_workitem_assignment.rb
│ │ │ ├── resume.rb
│ │ │ ├── set_workitem_assignments.rb
│ │ │ ├── start_case.rb
│ │ │ ├── start_workitem.rb
│ │ │ ├── suspend.rb
│ │ │ ├── sweep_automatic_transitions.rb
│ │ │ ├── sweep_timed_transitions.rb
│ │ │ └── workitem_action.rb
│ │ ├── comment.rb
│ │ ├── demo_target.rb
│ │ ├── entry.rb
│ │ ├── field.rb
│ │ ├── field_value.rb
│ │ ├── form.rb
│ │ ├── group.rb
│ │ ├── guard.rb
│ │ ├── lola.rb
│ │ ├── multiple_instances/
│ │ │ └── all_finish.rb
│ │ ├── party.rb
│ │ ├── place.rb
│ │ ├── token.rb
│ │ ├── transition.rb
│ │ ├── transition_static_assignment.rb
│ │ ├── user.rb
│ │ ├── workflow.rb
│ │ ├── workitem.rb
│ │ └── workitem_assignment.rb
│ └── views/
│ ├── layouts/
│ │ └── wf/
│ │ ├── _alert.html.erb
│ │ ├── _footer.html.erb
│ │ ├── _nav.html.erb
│ │ ├── _notice.html.erb
│ │ └── application.html.erb
│ └── wf/
│ ├── arcs/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── cases/
│ │ ├── _form.html.erb
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── comments/
│ │ └── new.html.erb
│ ├── fields/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── forms/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── guards/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── places/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── static_assignments/
│ │ ├── _form.html.erb
│ │ └── new.html.erb
│ ├── transitions/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── workflows/
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
│ ├── workitem_assignments/
│ │ └── new.html.erb
│ └── workitems/
│ ├── index.html.erb
│ ├── pre_finish.html.erb
│ └── show.html.erb
├── bin/
│ └── rails
├── config/
│ └── routes.rb
├── db/
│ └── migrate/
│ ├── 20200130201043_init.rb
│ ├── 20200130201641_init_some_data.rb
│ ├── 20200131200455_create_wf_entries.rb
│ ├── 20200201001543_add_target_field_name_for_guard.rb
│ ├── 20200212120019_remove_targetable_from_workitem.rb
│ ├── 20200213085258_add_formable.rb
│ ├── 20200213125753_add_form_id_for_entry.rb
│ ├── 20200213130900_remove_workflow_id_from_form_related.rb
│ ├── 20200220070839_remove_unused_column.rb
│ ├── 20200220072512_add_sub_workflow.rb
│ ├── 20200222150432_add_multi_instance.rb
│ └── 20200226195134_add_dynamic_assign_by.rb
├── lib/
│ ├── tasks/
│ │ └── wf_tasks.rake
│ ├── wf/
│ │ ├── engine.rb
│ │ └── version.rb
│ └── wf.rb
├── lola.md
├── screenshots/
│ └── .keep
├── test/
│ ├── controllers/
│ │ └── wf/
│ │ ├── arcs_controller_test.rb
│ │ ├── cases_controller_test.rb
│ │ ├── comments_controller_test.rb
│ │ ├── fields_controller_test.rb
│ │ ├── forms_controller_test.rb
│ │ ├── guards_controller_test.rb
│ │ ├── places_controller_test.rb
│ │ ├── static_assignments_controller_test.rb
│ │ ├── transitions_controller_test.rb
│ │ ├── workflows_controller_test.rb
│ │ ├── workitem_assignments_controller_test.rb
│ │ └── workitems_controller_test.rb
│ ├── dummy/
│ │ ├── .ruby-version
│ │ ├── Rakefile
│ │ ├── app/
│ │ │ ├── assets/
│ │ │ │ ├── config/
│ │ │ │ │ └── manifest.js
│ │ │ │ ├── images/
│ │ │ │ │ └── .keep
│ │ │ │ └── stylesheets/
│ │ │ │ └── application.css
│ │ │ ├── channels/
│ │ │ │ └── application_cable/
│ │ │ │ ├── channel.rb
│ │ │ │ └── connection.rb
│ │ │ ├── controllers/
│ │ │ │ ├── application_controller.rb
│ │ │ │ └── concerns/
│ │ │ │ └── .keep
│ │ │ ├── helpers/
│ │ │ │ └── application_helper.rb
│ │ │ ├── javascript/
│ │ │ │ └── packs/
│ │ │ │ └── application.js
│ │ │ ├── jobs/
│ │ │ │ └── application_job.rb
│ │ │ ├── mailers/
│ │ │ │ └── application_mailer.rb
│ │ │ ├── models/
│ │ │ │ ├── application_record.rb
│ │ │ │ ├── concerns/
│ │ │ │ │ └── .keep
│ │ │ │ ├── entry.rb
│ │ │ │ ├── field.rb
│ │ │ │ ├── field_value.rb
│ │ │ │ └── form.rb
│ │ │ └── views/
│ │ │ └── layouts/
│ │ │ ├── application.html.erb
│ │ │ ├── mailer.html.erb
│ │ │ └── mailer.text.erb
│ │ ├── bin/
│ │ │ ├── rails
│ │ │ ├── rake
│ │ │ └── setup
│ │ ├── config/
│ │ │ ├── application.rb
│ │ │ ├── boot.rb
│ │ │ ├── cable.yml
│ │ │ ├── database.yml
│ │ │ ├── environment.rb
│ │ │ ├── environments/
│ │ │ │ ├── development.rb
│ │ │ │ ├── production.rb
│ │ │ │ └── test.rb
│ │ │ ├── initializers/
│ │ │ │ ├── application_controller_renderer.rb
│ │ │ │ ├── assets.rb
│ │ │ │ ├── backtrace_silencers.rb
│ │ │ │ ├── content_security_policy.rb
│ │ │ │ ├── cookies_serializer.rb
│ │ │ │ ├── filter_parameter_logging.rb
│ │ │ │ ├── inflections.rb
│ │ │ │ ├── mime_types.rb
│ │ │ │ ├── my_assignment_callback.rb
│ │ │ │ ├── wf_config.rb
│ │ │ │ └── wrap_parameters.rb
│ │ │ ├── locales/
│ │ │ │ └── en.yml
│ │ │ ├── mysql_database.yml
│ │ │ ├── puma.rb
│ │ │ ├── routes.rb
│ │ │ ├── spring.rb
│ │ │ └── storage.yml
│ │ ├── config.ru
│ │ ├── db/
│ │ │ ├── migrate/
│ │ │ │ ├── 20200213081814_new_form.rb
│ │ │ │ ├── 20200213133942_add_form_id_in_entry1.rb
│ │ │ │ └── 20200214005535_add_entry_id_for_field_values1.rb
│ │ │ ├── schema.rb
│ │ │ └── seeds.rb
│ │ ├── lib/
│ │ │ └── assets/
│ │ │ └── .keep
│ │ ├── log/
│ │ │ └── .keep
│ │ ├── public/
│ │ │ ├── 404.html
│ │ │ ├── 422.html
│ │ │ └── 500.html
│ │ └── storage/
│ │ └── .keep
│ ├── fixtures/
│ │ └── wf/
│ │ ├── case_assignments.yml
│ │ ├── comments.yml
│ │ ├── demo_targets.yml
│ │ ├── entries.yml
│ │ ├── field_values.yml
│ │ ├── fields.yml
│ │ ├── forms.yml
│ │ ├── guards.yml
│ │ ├── parties.yml
│ │ ├── transition_static_assignments.yml
│ │ ├── users.yml
│ │ └── workitem_assignments.yml
│ ├── integration/
│ │ └── navigation_test.rb
│ ├── models/
│ │ └── wf/
│ │ ├── case_assignment_test.rb
│ │ ├── comment_test.rb
│ │ ├── demo_target_test.rb
│ │ ├── entry_test.rb
│ │ ├── field_test.rb
│ │ ├── field_value_test.rb
│ │ ├── form_test.rb
│ │ ├── guard_test.rb
│ │ ├── party_test.rb
│ │ ├── transition_static_assignment_test.rb
│ │ ├── user_test.rb
│ │ ├── wf_test.rb
│ │ └── workitem_assignment_test.rb
│ └── test_helper.rb
└── wf.gemspec
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
root = true
[*]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
end_of_line = lf
================================================
FILE: .gitattributes
================================================
*.rb diff=ruby
*.gemspec diff=ruby
================================================
FILE: .github/workflows/ci.yml
================================================
name: Testing
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
services:
db:
image: postgres:11
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v2
- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
ruby-version: "2.6"
- name: Build and test with Rake
env:
DATABASE_URL: "postgresql://postgres:postgres@127.0.0.1:5432/postgres"
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
RAILS_ENV: test
run: |
sudo apt-get -yqq install libpq-dev
sudo apt-get install libmysqlclient-dev
sudo apt-get install graphviz
gem install bundler
bundle install --jobs 4 --retry 3
bundle exec rake app:wf
bundle exec rails app:db:create && bundle exec rails app:db:migrate && bundle exec rails test
================================================
FILE: .github/workflows/gempush.yml
================================================
name: Ruby Gem
on:
push:
tags:
- v*
jobs:
build:
name: Build + Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
ruby-version: "2.6.x"
- name: Publish to RubyGems
run: |
mkdir -p $HOME/.gem
touch $HOME/.gem/credentials
chmod 0600 $HOME/.gem/credentials
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
gem build *.gemspec
gem push *.gem
env:
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.idea
.bundle/
# Ignore the default SQLite database.
test/dummy/db/*.sqlite3
test/dummy/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
log/*.log
!test/dummy/log/.keep
!test/dummy/tmp/.keep
test/dummy/log/*.log
test/dummy/tmp/
# Ignore uploaded files in development
test/dummy/storage/*
!test/dummy/storage/.keep
# Ignore compiled mruby in dummy app
/test/dummy/mruby/bin
pkg/
.byebug_history
node_modules/
test/dummy/public/packs
test/dummy/node_modules/
yarn-error.log
*.gem
.env
================================================
FILE: .rubocop.yml
================================================
require:
- rubocop-performance
- rubocop-rails
AllCops:
TargetRubyVersion: 2.5
Exclude:
- bin/**/*
- test/dummy/bin/**/*
- test/dummy/db/schema.rb
Rails:
Enabled: true
Layout/LineLength:
Max: 150
Metrics/MethodLength:
Max: 100
Metrics/BlockLength:
Max: 50
Metrics/ClassLength:
Enabled: false
Style/GuardClause:
Enabled: false
Style/Documentation:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Naming/AccessorMethodName:
Enabled: false
Naming/MemoizedInstanceVariableName:
Enabled: false
# Prefer assert_not over assert !
Rails/AssertNot:
Include:
- 'test/**/*'
# Prefer assert_not_x over refute_x
Rails/RefuteMethods:
Include:
- 'test/**/*'
# Prefer &&/|| over and/or.
Style/AndOr:
Enabled: true
# Do not use braces for hash literals when they are the last argument of a
# method call.
Style/BracesAroundHashParameters:
Enabled: true
EnforcedStyle: context_dependent
# Align `when` with `case`.
Layout/CaseIndentation:
Enabled: true
# Align comments with method definitions.
Layout/CommentIndentation:
Enabled: true
Layout/ElseAlignment:
Enabled: true
# Align `end` with the matching keyword or starting expression except for
# assignments, where it should be aligned with the LHS.
Layout/EndAlignment:
Enabled: true
EnforcedStyleAlignWith: variable
AutoCorrect: true
Layout/EmptyLineAfterMagicComment:
Enabled: true
Layout/EmptyLinesAroundBlockBody:
Enabled: true
# In a regular class definition, no empty lines around the body.
Layout/EmptyLinesAroundClassBody:
Enabled: true
# In a regular method definition, no empty lines around the body.
Layout/EmptyLinesAroundMethodBody:
Enabled: true
# In a regular module definition, no empty lines around the body.
Layout/EmptyLinesAroundModuleBody:
Enabled: true
Layout/FirstArgumentIndentation:
Enabled: true
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
Style/HashSyntax:
Enabled: true
# Method definitions after `private` or `protected` isolated calls need one
# extra level of indentation.
Layout/IndentationConsistency:
Enabled: true
EnforcedStyle: indented_internal_methods
# Two spaces, no tabs (for indentation).
Layout/IndentationWidth:
Enabled: true
Layout/LeadingCommentSpace:
Enabled: true
Layout/SpaceAfterColon:
Enabled: true
Layout/SpaceAfterComma:
Enabled: true
Layout/SpaceAfterSemicolon:
Enabled: true
Layout/SpaceAroundEqualsInParameterDefault:
Enabled: true
Layout/SpaceAroundKeyword:
Enabled: true
Layout/SpaceAroundOperators:
Enabled: true
Layout/SpaceBeforeComma:
Enabled: true
Layout/SpaceBeforeFirstArg:
Enabled: true
Style/DefWithParentheses:
Enabled: true
# Defining a method with parameters needs parentheses.
Style/MethodDefParentheses:
Enabled: true
Style/FrozenStringLiteralComment:
Enabled: true
EnforcedStyle: always
Style/RedundantFreeze:
Enabled: true
# Use `foo {}` not `foo{}`.
Layout/SpaceBeforeBlockBraces:
Enabled: true
# Use `foo { bar }` not `foo {bar}`.
Layout/SpaceInsideBlockBraces:
Enabled: true
EnforcedStyleForEmptyBraces: space
# Use `{ a: 1 }` not `{a:1}`.
Layout/SpaceInsideHashLiteralBraces:
Enabled: true
Layout/SpaceInsideParens:
Enabled: true
# Check quotes usage according to lint rule below.
Style/StringLiterals:
Enabled: true
EnforcedStyle: double_quotes
# Detect hard tabs, no hard tabs.
Layout/Tab:
Enabled: true
# Blank lines should not have any spaces.
Layout/TrailingEmptyLines:
Enabled: true
# No trailing whitespace.
Layout/TrailingWhitespace:
Enabled: true
# Use quotes for string literals when they are enough.
Style/RedundantPercentQ:
Enabled: true
Lint/AmbiguousOperator:
Enabled: true
Lint/AmbiguousRegexpLiteral:
Enabled: true
Lint/ErbNewArguments:
Enabled: true
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
Lint/RequireParentheses:
Enabled: true
Lint/ShadowingOuterLocalVariable:
Enabled: true
Lint/RedundantStringCoercion:
Enabled: true
Lint/UriEscapeUnescape:
Enabled: true
Lint/UselessAssignment:
Enabled: true
Lint/DeprecatedClassMethods:
Enabled: true
Style/ParenthesesAroundCondition:
Enabled: true
Style/RedundantBegin:
Enabled: true
Style/RedundantReturn:
Enabled: true
AllowMultipleReturnValues: true
Style/Semicolon:
Enabled: true
AllowAsExpressionSeparator: true
# Prefer Foo.method over Foo::method
Style/ColonMethodCall:
Enabled: true
Style/TrivialAccessors:
Enabled: true
Performance/FlatMap:
Enabled: true
Performance/RedundantMerge:
Enabled: true
Performance/StartWith:
Enabled: true
Performance/EndWith:
Enabled: true
Performance/RegexpMatch:
Enabled: true
Performance/ReverseEach:
Enabled: true
Performance/UnfreezeString:
Enabled: true
================================================
FILE: FormSpec.md
================================================
## Why
The core of Petri Flow is the workflow engine.
However, workflow, dynamic forms, and organization systems are inseparable. Therefore, petri flow provides simple built-in dynamic form functions, but in practice dynamic forms require more complex features.
Such as selecting data from other data sources, data validation, application-oriented data fields, UI customization, etc.
Petri Flow abstracts the interface needed to integrate with dynamic forms, in order to integrate more complex dynamic forms systems, such as [form core](https://github.com/rails-engine/form_core).
## Core Entity
* Form
* Field
* Entry
* FieldValue
## Relations
* form has_many fields
* form has_many entries
* field belongs_to form
* field_value belongs_to form
* field_value belongs_to field
* field_value belongs_to entry
* entry belongs_to form
* entry belongs_to user
* entry belongs_to workitem
* entry has_many field_values
## Casting
* Field#cast(value)
* FieldValue#value_after_cast
## Setting
```ruby
# config/initializers/wf_config.rb
Wf.user_class = "::Wf::User"
WF.form_class = "::Form"
Wf.entry_class = "::Entry"
Wf.field_class = "::Field"
```
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Declare your gem's dependencies in wf.gemspec.
# Bundler will treat runtime dependencies like base dependencies, and
# development dependencies will be added by default to the :development group.
gemspec
# Declare any dependencies that are still in development here instead of in
# your gemspec. These might include edge Rails or gems from your path or
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.
# To use a debugger
# gem 'byebug', group: [:development, :test]
gem "annotate"
gem "bootstrap", "~> 4.4.1"
gem "bootstrap4-kaminari-views"
gem "jquery-rails"
gem "kaminari"
gem "pg"
gem "pry-rails"
gem "simple_command"
gem "loaf"
gem "mysql2"
gem "rubocop"
gem "rubocop-performance"
gem "rubocop-rails"
gem "ruby-graphviz", require: "graphviz"
================================================
FILE: Guard.md
================================================
## Guard Expression
There are two per-defined variables for your guard expression, `workitem`, `target`.
Schema for `workitem`:
```json
{
"id":1,
"case_id":1,
"workflow_id":10,
"transition_id":24,
"state":"enabled",
"enabled_at":"2020-02-24T12:37:28.459Z",
"started_at":null,
"canceled_at":null,
"finished_at":null,
"overridden_at":null,
"deadline":null,
"created_at":"2020-02-24T12:37:28.601Z",
"updated_at":"2020-02-24T12:37:28.601Z",
"trigger_time":null,
"holding_user_id":null,
"children_count":3,
"children_finished_count":1,
"forked":false,
"parent_id":null,
"holding_user":{
},
"form":{
},
"children":[
{
"id":3,
"case_id":1,
"workflow_id":10,
"transition_id":24,
"state":"enabled",
"enabled_at":"2020-02-24T12:37:28.459Z",
"started_at":null,
"canceled_at":null,
"finished_at":null,
"overridden_at":null,
"deadline":null,
"created_at":"2020-02-24T12:37:28.757Z",
"updated_at":"2020-02-24T12:37:28.757Z",
"trigger_time":null,
"holding_user_id":"7",
"children_count":0,
"children_finished_count":0,
"forked":true,
"parent_id":1,
"holding_user":{
"id":7,
"name":"User6",
"created_at":"2020-02-24T12:36:46.994Z",
"updated_at":"2020-02-24T12:36:46.994Z",
"group_id":2
},
"form":{
},
"children":[
]
},
{
"id":4,
"case_id":1,
"workflow_id":10,
"transition_id":24,
"state":"enabled",
"enabled_at":"2020-02-24T12:37:28.459Z",
"started_at":null,
"canceled_at":null,
"finished_at":null,
"overridden_at":null,
"deadline":null,
"created_at":"2020-02-24T12:37:28.788Z",
"updated_at":"2020-02-24T12:37:28.788Z",
"trigger_time":null,
"holding_user_id":"5",
"children_count":0,
"children_finished_count":0,
"forked":true,
"parent_id":1,
"holding_user":{
"id":5,
"name":"User4",
"created_at":"2020-02-24T12:36:46.984Z",
"updated_at":"2020-02-24T12:36:46.984Z",
"group_id":1
},
"form":{
},
"children":[
]
},
{
"id":2,
"case_id":1,
"workflow_id":10,
"transition_id":24,
"state":"finished",
"enabled_at":"2020-02-24T12:37:28.459Z",
"started_at":null,
"canceled_at":null,
"finished_at":"2020-02-24T12:37:46.221Z",
"overridden_at":null,
"deadline":null,
"created_at":"2020-02-24T12:37:28.700Z",
"updated_at":"2020-02-24T12:37:46.232Z",
"trigger_time":null,
"holding_user_id":"1",
"children_count":0,
"children_finished_count":0,
"forked":true,
"parent_id":1,
"holding_user":{
"id":1,
"name":"User0",
"created_at":"2020-02-24T12:36:46.963Z",
"updated_at":"2020-02-24T12:36:46.963Z",
"group_id":4
},
"form":{
"score":90
},
"children":[
]
}
]
}
```
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Hooopo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MIT-LICENSE
================================================
Copyright 2020 Hooopo Wang
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Petri Flow  
Workflow engine for Rails.
## Features
* Full petri net features support (seq, parallel, iterative, timed, automitic etc.)
* Both approval workflow and business workflow.
* Simple web admin for workflow definition and case management.
* Build-in simple dynamic form.
* Replaceable dynamic form.
* Support sub workflow.
* Graph screen for workflow definition.
* Graph screen for case and token migration.
* Powerful guard expression.
* MySQL and Postgres Support.
* Powerful assignment management.
* Flexible integration of organizational structure system(role, group, position or department etc.)
## Docs
* [Petri-Nets and Workflows](https://hooopo.gitbook.io/petri-flow/)
* [Workflow Conceptual Guide](https://hooopo.gitbook.io/petri-flow/workflow-conceptual-guide)
* [Workflow Concepts Reference](https://hooopo.gitbook.io/petri-flow/workflow-concepts-reference)
* [Petri Flow ERD](https://hooopo.gitbook.io/petri-flow/erd)
* [Developer Doc](https://hooopo.gitbook.io/petri-flow/developer-document)
## Screenshots
### iterative routing

### parallel_routing

### guard

### case state graph

###
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'petri_flow', require: 'wf'
```
And then execute:
```bash
$ bundle
```
Install graphviz
```
brew install graphviz
```
Migration:
```
bundle exec rake wf:install:migrations
bundle exec rails db:create
bundle exec rails db:migrate
bundle exec rails db:seed
```
## Usage
Add wf_config:
```ruby
# config/initializers/wf_config.rb
Wf.user_class = "::User"
Wf.org_classes = { group: "::Group" }
```
Set parties:
For normal org model, for example group or role etc.
```ruby
module Wf
class Group < ApplicationRecord
has_many :users
include Wf::ActsAsParty
acts_as_party(user: false, party_name: :name)
end
end
```
For user model:
```ruby
module Wf
class User < ApplicationRecord
belongs_to :group, optional: true
include Wf::ActsAsParty
acts_as_party(user: true, party_name: :name)
end
end
```
then
```
bundle exec rails
```
visit:
```
http://localhost:3000/wf
```
## Testing
* RAILS_ENV=test rake app:db:migrate && RAILS_ENV=test rake app:db:test:prepare && bundle exec rake test
## Contributing
Contribution directions go here.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
================================================
FILE: Rakefile
================================================
# frozen_string_literal: true
begin
require "bundler/setup"
rescue LoadError
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
end
require "rdoc/task"
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = "rdoc"
rdoc.title = "Wf"
rdoc.options << "--line-numbers"
rdoc.rdoc_files.include("README.md")
rdoc.rdoc_files.include("lib/**/*.rb")
end
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
load "rails/tasks/engine.rake"
load "rails/tasks/statistics.rake"
require "bundler/gem_tasks"
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.pattern = "test/**/*_test.rb"
t.verbose = false
end
task default: :test
================================================
FILE: acts_as_party.md
================================================
## Usage
for normal org model, for example group or role etc.
```ruby
module Wf
class Group < ApplicationRecord
has_many :users
include Wf::ActsAsParty
acts_as_party(user: false, party_name: :name)
end
end
```
for user model:
```ruby
module Wf
class User < ApplicationRecord
belongs_to :group, optional: true
include Wf::ActsAsParty
acts_as_party(user: true, party_name: :name)
end
end
```
================================================
FILE: app/assets/config/wf_manifest.js
================================================
//= link_directory ../stylesheets/wf .css
//= link_directory ../javascripts/wf .js
================================================
FILE: app/assets/images/wf/.keep
================================================
================================================
FILE: app/assets/javascripts/wf/application.js
================================================
//= require jquery3
//= require popper
//= require bootstrap
//= require rails-ujs
//= require select2-full
================================================
FILE: app/assets/stylesheets/wf/application.scss
================================================
/*
*= require select2
*= require select2-bootstrap4
*/
@import "uikit/index";
main{
min-height: 80vh;
h2 {
margin-bottom: 1rem;
}
}
.footer{
text-align: center;
.container{
border-top: 1px solid rgb(245, 239, 228);
padding-top: 1rem;
}
a{
color: $gray-10;
}
}
================================================
FILE: app/assets/stylesheets/wf/arcs.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/cases.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/comments.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/fields.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/forms.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/guards.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/places.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/static_assignments.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/transitions.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/uikit/_colors.scss
================================================
// fork from: https://github.com/oortcast/42page/blob/master/app/javascript/42design/
//
// inspired by https://github.com/ant-design/ant-design
//
// The [colorPalette] means that the color is
// generated by https://ant.design/docs/spec/colors#Palette-Generation-Tool
// Brand Color
$pagegreen-base: #00d192;
$pagegreen-1: #f6fdfb; // tint background color
$pagegreen-2: #cff5e8; // background color
$pagegreen-3: #77f7c4; // [colorPalette]
$pagegreen-4: #4bebb0; // [colorPalette]
$pagegreen-5: #20CF97; // hover
$pagegreen-6: $pagegreen-base; // normal
$pagegreen-7: #00ab74; // click
$pagegreen-8: #008566; // [colorPalette]
$pagegreen-9: #005e4b; // [colorPalette]
$pagegreen-10: #00382f; // [colorPalette]
// Neutral Color
$gray-1: #fff;
$gray-2: #f7f7f7; //
$gray-3: #f1f1f2; // table header
$gray-4: #d2d3d4; // disable background
$gray-5: #92989c; // border
$gray-6: #868b8f; // disable text
$gray-7: #7a7e81; // secondary text
$gray-8: #53585c;
$gray-9: #23292f; // primary text
$gray-10: #000; // title
// Color Palette
$red-base: #fa4a4a;
$red-1: #fff2f0; // [colorPalette]
$red-2: #fff2f0;
$red-3: #ffcdc7; // [colorPalette]
$red-4: #ffa59e; // [colorPalette]
$red-5: #ff7a75; // [colorPalette]
$red-6: $red-base;
$red-7: #d4353a; // [colorPalette]
$red-8: #ad232c; // [colorPalette]
$red-9: #871420; // [colorPalette]
$red-10: #610e19; // [colorPalette]
$yellow-base: #F9C84A;
$yellow-1: #fffdf0; // [colorPalette]
$yellow-2: #FFF3E0;
$yellow-3: #fff5c7; // [colorPalette]
$yellow-4: #ffea9e; // [colorPalette]
$yellow-5: #ffdd75; // mark
$yellow-6: $yellow-base;
$yellow-7: #d4a135; // [colorPalette]
$yellow-8: #ad7d23; // [colorPalette]
$yellow-9: #875b14; // [colorPalette]
$yellow-10: #613e0e; // [colorPalette]
$blue-base: #3d90eb;
$blue-1: #f0faff; // [colorPalette]
$blue-2: #e6f5ff;
$blue-3: #bde3ff; // [colorPalette]
$blue-4: #94cfff; // [colorPalette]
$blue-5: #68b2f7; // [colorPalette]
$blue-6: $blue-base;
$blue-7: #296fc4; // [colorPalette]
$blue-8: #19519e; // [colorPalette]
$blue-9: #0d3678; // [colorPalette]
$blue-10: #082252; // [colorPalette]
$cyan-base: #01C1B2;
$cyan-1: #e6fff9; // [colorPalette]
$cyan-2: #E3FFF8;
$cyan-3: #72e8d2; // [colorPalette]
$cyan-4: #48dbc5; // [colorPalette]
$cyan-5: #23cfbb; // [colorPalette]
$cyan-6: $cyan-base;
$cyan-7: #009c94; // [colorPalette]
$cyan-8: #007573; // [colorPalette]
$cyan-9: #004e4f; // [colorPalette]
$cyan-10: #002729; // [colorPalette]
================================================
FILE: app/assets/stylesheets/wf/uikit/_variables.scss
================================================
@import "colors";
@import "bootstrap/functions";
$blue: $blue-base;
$red: $red-base;
$yellow: $yellow-base;
$green: $pagegreen-base;
$cyan: $cyan-base;
$primary: $green;
$gray-100: $gray-1;
$gray-200: $gray-2;
$gray-300: $gray-3; // table header
$gray-400: $gray-4; // disable background
$gray-500: $gray-5; // border
$gray-600: $gray-6; // disable text
$gray-700: $gray-7; // secondary text
$gray-800: $gray-8;
$gray-900: $gray-9; // primary text
$secondary: $gray-7;
$body-bg: #fffefd;
$link-color: $primary;
$link-hover-color: $pagegreen-5;
$font-family-sans-serif: system, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Noto Color Emoji", "Segoe UI Symbol";
$font-family-monospace: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
$line-height-base: 1.6;
@import "bootstrap/variables";
================================================
FILE: app/assets/stylesheets/wf/uikit/alert.scss
================================================
@import "variables";
.alert{
margin-bottom: 2rem;
p{
margin-bottom: 0;
}
&.alert-success{
background: $pagegreen-1;
border-color: $pagegreen-4;
color: $gray-9;
}
&.alert-warning{
background: $red-1;
border-color: $red-5;
color: $gray-9;
}
}
================================================
FILE: app/assets/stylesheets/wf/uikit/button.scss
================================================
@import "_variables";
.btn-light{
color: $gray-8;
background: rgba(94, 94, 94, 0.2);
border: rgb(94,94,94);
}
================================================
FILE: app/assets/stylesheets/wf/uikit/card.scss
================================================
.card{
margin-bottom: 4rem;
border: 1px solid rgba(230, 230, 230, 0.41);
border-radius: 0.625rem;
box-shadow: 0 2px 6px 0 rgba(0,0,0,0.05);
}
================================================
FILE: app/assets/stylesheets/wf/uikit/index.scss
================================================
@import "_variables";
@import "bootstrap";
//overwrite
@import "navbar";
@import "table";
@import "card";
@import "alert";
@import "button";
================================================
FILE: app/assets/stylesheets/wf/uikit/navbar.scss
================================================
@import "variables";
.navbar-petri{
box-shadow: 0 12px 24px 0 rgba(0,0,0,0.05);
margin-bottom: 2rem;
background: $white;
.navbar-brand a{
color: $gray-9;
&:hover{
text-decoration: none;
color: $gray-10;
}
}
.navbar-end a{
color: #7b7f82;
margin: 0 20px;
font-size: 18px;
border-bottom: transparent solid 2px;
&:hover{
color: $gray-9;
text-decoration: none;
}
&.is-active{
color: $gray-9;
border-bottom: $primary solid 2px;
}
}
}
================================================
FILE: app/assets/stylesheets/wf/uikit/table.scss
================================================
@import "variables";
.table-view{
thead th{
color: $gray-7;
font-weight: 500;
border-top: 0;
border-bottom-width: 1px;
border-color: $gray-3;
}
tbody tr:hover {
background-color: $gray-2;
}
tbody td a:not(.btn) {
color: $gray-10;
}
}
================================================
FILE: app/assets/stylesheets/wf/workflows.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/workitem_assignments.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/assets/stylesheets/wf/workitems.css
================================================
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/
================================================
FILE: app/controllers/wf/application_controller.rb
================================================
# frozen_string_literal: true
module Wf
class ApplicationController < ::ApplicationController
protect_from_forgery with: :exception
helper_method :wf_current_user
breadcrumb "Home", :root_path
def wf_current_user
current_user
end
end
end
================================================
FILE: app/controllers/wf/arcs_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class ArcsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.new
breadcrumb @workflow.name, workflow_path(@workflow)
end
def create
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.new(arc_params)
if @arc.save
redirect_to workflow_path(@workflow), notice: "arc was successfully created."
else
render :new
end
end
def destroy
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.find(params[:id])
@arc.destroy
render js: "window.location.reload()"
end
def show
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def edit
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
breadcrumb @arc.name, workflow_arc_path(@workflow, @arc)
end
def update
@workflow = Wf::Workflow.find(params[:workflow_id])
@arc = @workflow.arcs.find(params[:id])
if @arc.update(arc_params)
redirect_to workflow_path(@workflow), notice: "arc was successfully created."
else
render :edit
end
end
private
def arc_params
params.fetch(:arc, {}).permit(:direction, :transition_id, :place_id)
end
end
end
================================================
FILE: app/controllers/wf/cases_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class CasesController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workflow = Wf::Workflow.find(params[:workflow_id])
@wf_case = @workflow.cases.new
breadcrumb @workflow.name, workflow_path(@workflow)
end
def create
@workflow = Wf::Workflow.find(params[:workflow_id])
@wf_case = Wf::CaseCommand::New.call(@workflow, GlobalID::Locator.locate(case_params[:targetable])).result
Wf::CaseCommand::StartCase.call(@wf_case)
redirect_to workflow_cases_path(@workflow), notice: "case created."
end
def index
@workflow = Wf::Workflow.find(params[:workflow_id])
@cases = @workflow.cases.order("id DESC")
@cases = @cases.where(state: params[:state].intern) if params[:state].present?
@cases = @cases.page(params[:page])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def show
@workflow = Wf::Workflow.find(params[:workflow_id])
@wf_case = @workflow.cases.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def destroy
@workflow = Wf::Workflow.find(params[:workflow_id])
@case = @workflow.cases.find(params[:id])
@case.destroy
render js: "window.location.reload()"
end
private
def case_params
params.fetch(:case, {}).permit(:targetable, :target_id, :target_type)
end
end
end
================================================
FILE: app/controllers/wf/comments_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class CommentsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workitem = Wf::Workitem.find(params[:workitem_id])
@comment = @workitem.comments.new
breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
breadcrumb @workitem.name, workitem_path(@workitem)
end
def create
@workitem = Wf::Workitem.find(params[:workitem_id])
Wf::CaseCommand::AddComment.call(@workitem, params[:comment][:body], wf_current_user)
redirect_to workitem_path(@workitem), notice: "Comment Added."
end
def destroy
@workitem = Wf::Workitem.find(params[:workitem_id])
@comment = @workitem.comments.find(params[:id])
@comment.destroy
render js: "window.location.reload()"
end
end
end
================================================
FILE: app/controllers/wf/fields_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class FieldsController < ApplicationController
breadcrumb "Forms", :forms_path
def new
@form = Wf::Form.find(params[:form_id])
@field = @form.fields.new
breadcrumb @form.name, form_path(@form)
end
def create
@form = Wf::Form.find(params[:form_id])
@field = @form.fields.new(field_params)
if @field.save
redirect_to form_path(@form), notice: "field was successfully created."
else
render :new
end
end
def destroy
@form = Wf::Form.find(params[:form_id])
@field = @form.fields.find(params[:id])
@field.destroy
render js: "window.location.reload()"
end
def edit
@form = Wf::Form.find(params[:form_id])
@field = @form.fields.find(params[:id])
breadcrumb @form.name, form_path(@form)
end
def update
@form = Wf::Form.find(params[:form_id])
@field = @form.fields.find(params[:id])
if @field.update(field_params)
redirect_to form_path(@form), notice: "field was successfully created."
else
render :edit
end
end
private
def field_params
params.fetch(:field, {}).permit(:name, :form_id, :field_type, :position, :default_value)
end
end
end
================================================
FILE: app/controllers/wf/forms_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class FormsController < ApplicationController
breadcrumb "Forms", :forms_path
def index
@forms = Wf::Form.order("id DESC").page(params[:page])
end
def new
@form = Wf::Form.new
end
def edit
@form = Wf::Form.find(params[:id])
end
def show
@form = Wf::Form.find(params[:id])
end
def destroy
@form = Wf::Form.find(params[:id])
@form.destroy
respond_to do |format|
format.html { redirect_to forms_path, notice: "form was successfully deleted." }
format.js { render js: "window.location.reload();" }
end
end
def update
@form = Wf::Form.find(params[:id])
if @form.update(form_params)
redirect_to form_path(@form), notice: "form was successfully updated."
else
render :edit
end
end
def create
@form = Wf::Form.new(form_params)
if @form.save
redirect_to forms_path, notice: "form was successfully created."
else
render :new
end
end
private
def form_params
params.fetch(:form, {}).permit(:name, :description)
end
end
end
================================================
FILE: app/controllers/wf/guards_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class GuardsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@arc = Wf::Arc.find(params[:arc_id])
@guard = @arc.guards.new
breadcrumb @arc.workflow.name, workflow_path(@arc.workflow)
breadcrumb @arc.name, workflow_arc_path(@arc.workflow, @arc)
end
def create
@arc = Wf::Arc.find(params[:arc_id])
gp = guard_params.merge(fieldable: GlobalID::Locator.locate(guard_params[:fieldable]))
@guard = @arc.guards.new(gp.merge(workflow: @arc.workflow))
redirect_to workflow_arc_path(@arc.workflow, @arc), notice: "only out direction arc can set guard!" unless @arc.out?
if @guard.save
redirect_to workflow_arc_path(@arc.workflow, @arc), notice: "guard was successfully created."
else
render :new
end
end
def destroy
@arc = Wf::Arc.find(params[:arc_id])
@guard = @arc.guards.find(params[:id])
@guard.destroy
render js: "window.location.reload()"
end
def edit
@arc = Wf::Arc.find(params[:arc_id])
@guard = @arc.guards.find(params[:id])
breadcrumb @arc.workflow.name, workflow_path(@arc.workflow)
breadcrumb @arc.name, workflow_arc_path(@arc.workflow, @arc)
end
def update
@arc = Wf::Arc.find(params[:arc_id])
gp = guard_params.merge(fieldable: GlobalID::Locator.locate(guard_params[:fieldable]))
@guard = @arc.guards.find(params[:id])
if @guard.update(gp)
redirect_to workflow_arc_path(@arc.workflow, @arc), notice: "guard was successfully created."
else
render :edit
end
end
private
def guard_params
params.fetch(:guard, {}).permit(:fieldable, :fieldable_type, :fieldable_id, :op, :value, :exp)
end
end
end
================================================
FILE: app/controllers/wf/places_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class PlacesController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workflow = Wf::Workflow.find(params[:workflow_id])
@place = @workflow.places.new
breadcrumb @workflow.name, workflow_path(@workflow)
end
def create
@workflow = Wf::Workflow.find(params[:workflow_id])
@place = @workflow.places.new(place_params)
if @place.save
redirect_to workflow_path(@workflow), notice: "place was successfully created."
else
render :new
end
end
def destroy
@workflow = Wf::Workflow.find(params[:workflow_id])
@place = @workflow.places.find(params[:id])
@place.destroy
render js: "window.location.reload()"
end
def edit
@workflow = Wf::Workflow.find(params[:workflow_id])
@place = @workflow.places.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def update
@workflow = Wf::Workflow.find(params[:workflow_id])
@place = @workflow.places.find(params[:id])
if @place.update(place_params)
redirect_to workflow_path(@workflow), notice: "place was successfully created."
else
render :edit
end
end
private
def place_params
params.fetch(:place, {}).permit(:name, :description, :place_type, :sort_order)
end
end
end
================================================
FILE: app/controllers/wf/static_assignments_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class StaticAssignmentsController < ApplicationController
def new
@transition = Wf::Transition.find(params[:transition_id])
@static_assignment = @transition.transition_static_assignments.new
end
def create
@transition = Wf::Transition.find(params[:transition_id])
@party = Wf::Party.find(permit_params[:party_id])
@static_assignment = @transition.transition_static_assignments.new(party: @party)
if @static_assignment.save
redirect_to workflow_transition_path(@transition.workflow, @transition), notice: "static assignment was successfully created."
else
render :new
end
end
def destroy
@transition = Wf::Transition.find(params[:transition_id])
@static_assignment = @transition.transition_static_assignments.find(params[:id])
@static_assignment.destroy
render js: "window.location.reload()"
end
def permit_params
params.fetch(:transition_static_assignment, {}).permit(:party_id)
end
end
end
================================================
FILE: app/controllers/wf/transitions_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class TransitionsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workflow = Wf::Workflow.find(params[:workflow_id])
@transition = @workflow.transitions.new
breadcrumb @workflow.name, workflow_path(@workflow)
end
def show
@workflow = Wf::Workflow.find(params[:workflow_id])
@transition = @workflow.transitions.find(params[:id])
end
def create
@workflow = Wf::Workflow.find(params[:workflow_id])
tp = transition_params.merge(form: GlobalID::Locator.locate(transition_params[:form]))
@transition = @workflow.transitions.new(tp)
if @transition.save
redirect_to workflow_path(@workflow), notice: "transition was successfully created."
else
render :new
end
end
def edit
@workflow = Wf::Workflow.find(params[:workflow_id])
@transition = @workflow.transitions.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def destroy
@workflow = Wf::Workflow.find(params[:workflow_id])
@transition = @workflow.transitions.find(params[:id])
@transition.destroy
render js: "window.location.reload()"
end
def update
@workflow = Wf::Workflow.find(params[:workflow_id])
@transition = @workflow.transitions.find(params[:id])
tp = transition_params.merge(form: GlobalID::Locator.locate(transition_params[:form]))
if @transition.update(tp)
redirect_to workflow_path(@workflow), notice: "transition was successfully updated."
else
render :edit
end
end
private
def transition_params
params.fetch(:transition, {}).permit(
:name,
:description,
:trigger_limit,
:trigger_type,
:sort_order,
:form,
:enable_callback,
:fire_callback,
:time_callback,
:hold_timeout_callback,
:assignment_callback,
:unassignment_callback,
:notification_callback,
:deadline_callback,
:sub_workflow_id,
:multiple_instance,
:finish_condition,
:dynamic_assign_by_id
)
end
end
end
================================================
FILE: app/controllers/wf/workflows_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class WorkflowsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def index
@workflows = Wf::Workflow.order("id DESC").page(params[:page])
end
def new
@workflow = Wf::Workflow.new
end
def edit
@workflow = Wf::Workflow.find(params[:id])
breadcrumb @workflow.name, workflow_path(@workflow)
end
def show
@workflow = Wf::Workflow.find(params[:id])
end
def destroy
@workflow = Wf::Workflow.find(params[:id])
@workflow.destroy
respond_to do |format|
format.html { redirect_to workflows_path, notice: "workflow was successfully deleted." }
format.js { render js: "window.location.reload();" }
end
end
def update
@workflow = Wf::Workflow.find(params[:id])
if @workflow.update(workflow_params)
redirect_to workflow_path(@workflow), notice: "workflow was successfully updated."
else
render :edit
end
end
def create
@workflow = Wf::Workflow.new(workflow_params)
if @workflow.save
redirect_to workflows_path, notice: "workflow was successfully created."
else
render :new
end
end
private
def workflow_params
params.fetch(:workflow, {}).permit(:name, :description)
end
end
end
================================================
FILE: app/controllers/wf/workitem_assignments_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class WorkitemAssignmentsController < ApplicationController
breadcrumb "Workflows", :workflows_path
def new
@workitem = Wf::Workitem.find(params[:workitem_id])
@workitem_assignment = @workitem.workitem_assignments.new(party_id: params[:party_id])
breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
breadcrumb @workitem.name, workitem_path(@workitem)
end
def create
@workitem = Wf::Workitem.find(params[:workitem_id])
party = Wf::Party.find(params[:workitem_assignment][:party_id])
Wf::CaseCommand::AddWorkitemAssignment.call(@workitem, party)
redirect_to workitem_path(@workitem), notice: "assigned party to workitem."
end
def destroy
@workitem = Wf::Workitem.find(params[:workitem_id])
party = Wf::Party.find(params[:party_id])
Wf::CaseCommand::RemoveWorkitemAssignment.call(@workitem, party)
render js: "window.location.reload()"
end
end
end
================================================
FILE: app/controllers/wf/workitems_controller.rb
================================================
# frozen_string_literal: true
require_dependency "wf/application_controller"
module Wf
class WorkitemsController < ApplicationController
before_action :find_workitem, except: [:index]
before_action :check_start, only: [:start]
before_action :check_finish, only: %i[pre_finish finish]
breadcrumb "Workflows", :workflows_path
def index
@workitems = Wf::Workitem.todo(wf_current_user)
@workitems = @workitems.where(state: params[:state].intern) if params[:state]
@workitems = @workitems.where(state: params[:state].intern) if params[:state].present?
@workitems = @workitems.distinct.order("id desc").page(params[:page])
end
def show
breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
end
def start
Wf::CaseCommand::StartWorkitem.call(@workitem, wf_current_user)
breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
breadcrumb @workitem.name, workitem_path(@workitem)
render :pre_finish
end
def pre_finish
breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)
breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)
breadcrumb @workitem.name, workitem_path(@workitem)
end
def finish
if dynamic_assignments = params.dig(:workitem, :dynamic_assignments)
dynamic_assignments.permit!.each do |t_id, party_id|
Wf::CaseCommand::AddManualAssignment.call(@workitem.case, @workitem.workflow.transitions.find(t_id), Wf::Party.find(party_id))
end
end
if @workitem.transition.form && params[:workitem][:entry]
form = @workitem.transition.form
cmd = Wf::CaseCommand::CreateEntry.call(form, @workitem, wf_current_user, params[:workitem][:entry].permit!)
if cmd.success?
Wf::CaseCommand::FinishWorkitem.call(@workitem)
finish_and_redirect
else
redirect_to pre_finish_workitem_path(@workitem), notice: "Your input no OK."
end
else
Wf::CaseCommand::FinishWorkitem.call(@workitem)
finish_and_redirect
end
end
def finish_and_redirect
if @workitem.case.finished?
if started_by = @workitem.case.started_by_workitem
redirect_to workflow_case_path(started_by.workflow, started_by.case), notice: "workitem is done, and goto parent case."
else
redirect_to workflow_case_path(@workitem.workflow, @workitem.case), notice: "workitem is done, and the case is finished."
end
else
redirect_to workitem_path(@workitem.case.workitems.enabled.first), notice: "workitem is done, and goto next fireable workitem."
end
end
def find_workitem
@workitem = Wf::Workitem.find(params[:id])
end
def check_start
unless @workitem.started_by?(wf_current_user)
redirect_to workitem_path(@workitem), notice: "You can not start this workitem, Please assign to youself first."
end
end
def check_finish
unless @workitem.finished_by?(wf_current_user)
redirect_to workitem_path(@workitem), notice: "You can not the holding use of this workitem, Please assign to youself && start it first."
end
end
end
end
================================================
FILE: app/helpers/wf/application_helper.rb
================================================
# frozen_string_literal: true
module Wf
module ApplicationHelper
end
end
================================================
FILE: app/helpers/wf/arcs_helper.rb
================================================
# frozen_string_literal: true
module Wf
module ArcsHelper
end
end
================================================
FILE: app/helpers/wf/cases_helper.rb
================================================
# frozen_string_literal: true
module Wf
module CasesHelper
end
end
================================================
FILE: app/helpers/wf/comments_helper.rb
================================================
# frozen_string_literal: true
module Wf
module CommentsHelper
end
end
================================================
FILE: app/helpers/wf/fields_helper.rb
================================================
# frozen_string_literal: true
module Wf
module FieldsHelper
end
end
================================================
FILE: app/helpers/wf/forms_helper.rb
================================================
# frozen_string_literal: true
module Wf
module FormsHelper
end
end
================================================
FILE: app/helpers/wf/guards_helper.rb
================================================
# frozen_string_literal: true
module Wf
module GuardsHelper
end
end
================================================
FILE: app/helpers/wf/places_helper.rb
================================================
# frozen_string_literal: true
module Wf
module PlacesHelper
end
end
================================================
FILE: app/helpers/wf/static_assignments_helper.rb
================================================
# frozen_string_literal: true
module Wf
module StaticAssignmentsHelper
end
end
================================================
FILE: app/helpers/wf/transitions_helper.rb
================================================
# frozen_string_literal: true
module Wf
module TransitionsHelper
end
end
================================================
FILE: app/helpers/wf/workflows_helper.rb
================================================
# frozen_string_literal: true
module Wf
module WorkflowsHelper
end
end
================================================
FILE: app/helpers/wf/workitem_assignments_helper.rb
================================================
# frozen_string_literal: true
module Wf
module WorkitemAssignmentsHelper
end
end
================================================
FILE: app/helpers/wf/workitems_helper.rb
================================================
# frozen_string_literal: true
module Wf
module WorkitemsHelper
end
end
================================================
FILE: app/jobs/wf/application_job.rb
================================================
# frozen_string_literal: true
module Wf
class ApplicationJob < ActiveJob::Base
end
end
================================================
FILE: app/jobs/wf/fire_timed_workitem_job.rb
================================================
# frozen_string_literal: true
module Wf
class FireTimedWorkitemJob < ApplicationJob
queue_as :default
def perform(workitem_id)
item = Wf::Workitem.find(workitem_id)
if item.trigger_time && item.enabled? && item.case.active?
CaseCommand::FireTransitionInternal.call(item)
CaseCommand::SweepAutomaticTransitions.call(item.case)
end
end
end
end
================================================
FILE: app/mailers/wf/application_mailer.rb
================================================
# frozen_string_literal: true
module Wf
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout "mailer"
end
end
================================================
FILE: app/models/wf/acts_as_party.rb
================================================
# frozen_string_literal: true
require "active_support/concern"
module Wf
module ActsAsParty
extend ActiveSupport::Concern
included do
has_one :party, as: :partable
end
module ClassMethods
def acts_as_party(options = { user: false, party_name: :name })
cattr_accessor :yaffle_text_field
has_many :users, foreign_key: :id if options[:user]
after_create do
create_party(party_name: options[:party_name])
end
end
end
end
end
================================================
FILE: app/models/wf/application_record.rb
================================================
# frozen_string_literal: true
module Wf
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
end
================================================
FILE: app/models/wf/arc.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_arcs
#
# id :integer not null, primary key
# workflow_id :integer
# transition_id :integer
# place_id :integer
# direction :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# guards_count :integer default("0")
#
module Wf
class Arc < ApplicationRecord
belongs_to :workflow, touch: true
belongs_to :transition
belongs_to :place
has_many :guards, dependent: :destroy
scope :with_guards, -> { where("guards_count > 0") }
scope :without_guards, -> { where(guards_count: 0) }
# direction is relative to the transition
enum direction: {
in: 0,
out: 1
}
def name
if in?
[place&.name, transition&.name].join(" -> ")
else
[transition&.name, place&.name].join(" -> ")
end
end
end
end
================================================
FILE: app/models/wf/callbacks/assignment_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class AssignmentDefault < ApplicationJob
queue_as :default
def perform(_workitem_id)
# return Party array.
[]
end
end
end
================================================
FILE: app/models/wf/callbacks/deadline_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class DeadlineDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/enable_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class EnableDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/fire_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class FireDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/hold_timeout_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class HoldTimeoutDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/notification_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class NotificationDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/time_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class TimeDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/callbacks/unassignment_default.rb
================================================
# frozen_string_literal: true
module Wf::Callbacks
class UnassignmentDefault < ApplicationJob
queue_as :default
def perform(*guests)
$stdout.puts(guests.inspect)
end
end
end
================================================
FILE: app/models/wf/case.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_cases
#
# id :integer not null, primary key
# workflow_id :integer
# targetable_type :string
# targetable_id :string
# state :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# started_by_workitem_id :integer
#
module Wf
class Case < ApplicationRecord
belongs_to :workflow
belongs_to :targetable, optional: true, polymorphic: true
belongs_to :started_by_workitem, optional: true, class_name: "Wf::Workitem"
has_many :workitems
has_many :tokens
has_many :case_assignments
has_many :parties, through: :case_assignments, source: "party"
enum state: {
created: 0,
active: 1,
suspended: 2,
canceled: 3,
finished: 4
}
def can_fire?(transition)
ins = transition.arcs.in.to_a
return false if ins.blank?
ins.all? { |arc| arc.place.tokens.where(case: self).where(state: :free).exists? }
end
def name
"Case->#{id}"
end
end
end
================================================
FILE: app/models/wf/case_assignment.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_case_assignments
#
# id :integer not null, primary key
# case_id :integer
# transition_id :integer
# party_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# frozen_string_literal: true
module Wf
class CaseAssignment < ApplicationRecord
belongs_to :case
belongs_to :transition
belongs_to :party
end
end
================================================
FILE: app/models/wf/case_command/add_comment.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class AddComment
prepend SimpleCommand
attr_reader :workitem, :comment, :user
def initialize(workitem, comment, user)
@workitem = workitem
@comment = comment
@user = user
end
def call
workitem.comments.create!(user: user, body: comment)
end
end
end
================================================
FILE: app/models/wf/case_command/add_manual_assignment.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class AddManualAssignment
prepend SimpleCommand
attr_reader :wf_case, :transition, :party
def initialize(wf_case, transition, party)
@wf_case = wf_case
@transition = transition
@party = party
end
def call
wf_case.case_assignments.find_or_create_by!(transition: transition, party: party)
end
end
end
================================================
FILE: app/models/wf/case_command/add_token.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class AddToken
prepend SimpleCommand
attr_reader :wf_case, :place
def initialize(wf_case, place)
@wf_case = wf_case
@place = place
end
def call
wf_case.tokens.create!(
workflow: wf_case.workflow,
place: place,
state: :free
)
end
end
end
================================================
FILE: app/models/wf/case_command/add_workitem_assignment.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class AddWorkitemAssignment
prepend SimpleCommand
attr_reader :workitem, :party, :permanent
def initialize(workitem, party, permanent = true)
@workitem = workitem
@party = party
@permanent = permanent
end
def call
return if party.nil?
Wf::ApplicationRecord.transaction do
AddManualAssignment.call(workitem.case, workitem.transition, party) if permanent
notified_users = workitem.parties.map do |p|
p.partable.users.to_a
end.flatten
assign = workitem.workitem_assignments.where(party: party).first
break if assign
workitem.workitem_assignments.create!(party: party)
new_users = party.partable.users.to_a
to_notify = new_users - notified_users
transition = workitem.transition
to_notify.each do |user|
# TODO: multiple instance + sub workflow
if transition.multiple_instance? && !workitem.forked?
next if workitem.children.where(holding_user: user).exists?
child = workitem.children.create!(
workflow_id: workitem.workflow_id,
transition_id: workitem.transition_id,
state: :enabled,
trigger_time: workitem.trigger_time,
forked: true,
holding_user: user,
case_id: workitem.case_id
)
workitem.transition.notification_callback.constantize.new(child, user.id).perform_now
else
workitem.transition.notification_callback.constantize.new(workitem, user.id).perform_now
end
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/begin_workitem_action.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class BeginWorkitemAction
prepend SimpleCommand
attr_reader :workitem, :action, :user
def initialize(workitem, user, action = :start)
@workitem = workitem
@action = action
@user = user
end
def call
if action == :start
raise("Workitem is in state #{workitem.state}, but it must be in state enabled to be started.") unless workitem.enabled?
raise("You are not assigned to this workitem.") unless workitem.owned_by?(user)
elsif action == :finish || action == :cancel
if workitem.started?
raise("You are not the user currently working on this workitem.") if workitem.holding_user != user
elsif workitem.enabled?
raise("You can only cancel a workitem in state started, but this workitem is in state #{workitem.state}.") if action == :cancel
raise("You are not assigned to this workitem.") unless workitem.owned_by?(user)
workitem.update!(holding_user: user)
else
raise("Workitem is in state #{workitem.state}, but it must be in state enabled or started to be finished.")
end
elsif action == :comment
# TODO
end
end
end
end
================================================
FILE: app/models/wf/case_command/cancel.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class Cancel
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
raise("Only active or suspended cases can be canceled") unless wf_case.suspended? || wf_case.active?
wf_case.canceled!
end
end
end
================================================
FILE: app/models/wf/case_command/cancel_workitem.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class CancelWorkitem
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
raise("The workitem is not in state #{workitem.state}") unless workitem.started?
Wf::ApplicationRecord.transaction do
workitem.update!(state: :canceled, canceled_at: Time.zone.now)
ReleaseToken.call(workitem)
SweepAutomaticTransitions.call(workitem.case)
end
end
end
end
================================================
FILE: app/models/wf/case_command/clear_manual_assignments.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class ClearManualAssignments
prepend SimpleCommand
attr_reader :wf_case, :transition
def initialize(wf_case, transition)
@wf_case = wf_case
@transition = transition
end
def call
wf_case.case_assignments.where(transition: transition).find_each(&:destroy)
end
end
end
================================================
FILE: app/models/wf/case_command/clear_workitem_assignments.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class ClearWorkitemAssignments
prepend SimpleCommand
attr_reader :workitem, :permanent
def initialize(workitem, permanent = true)
@workitem = workitem
@permanent = permanent
end
def call
Wf::ApplicationRecord.transaction do
ClearManualAssignments.call(workitem.case, workitem.transition) if permanent
workitem.workitem_assignments.delete_all
workitem.transition.unassignment_callback.constantize.new(workitem.id).perform
end
end
end
end
================================================
FILE: app/models/wf/case_command/consume_token.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class ConsumeToken
prepend SimpleCommand
attr_reader :wf_case, :place, :locked_item
def initialize(wf_case, place, locked_item = nil)
@wf_case = wf_case
@place = place
@locked_item = locked_item
end
def call
Wf::ApplicationRecord.transaction do
if locked_item
wf_case.tokens.where(place: place, state: :locked, locked_workitem_id: locked_item.id).update(consumed_at: Time.zone.now, state: :consumed)
else
wf_case.tokens.where(id: wf_case.tokens.where(place: place, state: :free).first&.id).update(consumed_at: Time.zone.now, state: :consumed)
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/create_entry.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class CreateEntry
prepend SimpleCommand
attr_reader :form, :workitem, :user, :params
def initialize(form, workitem, user, params)
@form = form
@workitem = workitem
@params = params
@user = user
end
def call
create_entry
rescue StandardError
binding.pry
puts $ERROR_INFO
# TODO: more detail
errors.add(:base, :failure)
end
def create_entry
Wf::ApplicationRecord.transaction do
entry = form.entries.find_or_create_by!(user: user, workitem: workitem)
params.each do |field_id, field_value|
if field = entry.field_values.where(form: form, field_id: field_id).first
field.update!(value: field_value)
else
entry.field_values.create!(form: form, field_id: field_id, value: field_value)
end
end
entry.update_payload!
entry
end
end
end
end
================================================
FILE: app/models/wf/case_command/enable_transitions.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class EnableTransitions
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
Wf::ApplicationRecord.transaction do
wf_case.workitems.enabled.each do |workitem|
workitem.update!(state: :overridden, overridden_at: Time.zone.now) unless wf_case.can_fire?(workitem.transition)
end
wf_case.workflow.transitions.each do |transition|
next unless wf_case.can_fire?(transition) && !transition.workitems.where(case: wf_case, state: %i[enabled started]).exists?
trigger_time = Time.zone.now + transition.trigger_limit.minutes if transition.trigger_limit && transition.time?
workitem = wf_case.workitems.create!(
workflow: wf_case.workflow,
transition: transition,
state: :enabled,
trigger_time: trigger_time
)
Wf::FireTimedWorkitemJob.set(wait: transition.trigger_limit.minutes).perform_later(workitem.id) if trigger_time
SetWorkitemAssignments.call(workitem)
workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
if sub_workflow = transition.sub_workflow
sub_case = Wf::CaseCommand::New.call(sub_workflow, nil, workitem).result
Wf::CaseCommand::StartCase.call(sub_case)
end
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/end_workitem_action.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class EndWorkitemAction
prepend SimpleCommand
attr_reader :workitem, :action, :user
def initialize(workitem, user, action = :start)
@workitem = workitem
@action = action
@user = user
end
def call
if action == :start
StartWorkitem.call(workitem, user)
elsif action == :finish
FinishWorkitem.call(workitem, user)
elsif action == :cancel
CancelWorkitem.call(workitem, user)
elsif action == :comment
raise("Unknown action #{action}")
end
end
end
end
================================================
FILE: app/models/wf/case_command/finish_workitem.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class FinishWorkitem
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
Wf::ApplicationRecord.transaction do
if workitem.forked?
workitem.update!(finished_at: Time.zone.now, state: :finished)
Wf::Workitem.increment_counter(:children_finished_count, workitem.parent_id)
if parent = workitem.parent
if (parent.children_finished_count >= parent.children_count) || workitem.transition.finish_condition.constantize.new.perform(workitem)
parent.children.where(state: %i[started enabled]).find_each do |wi|
wi.update!(overridden_at: Time.zone.now, state: :overridden)
end
FireTransitionInternal.call(parent)
SweepAutomaticTransitions.call(parent.case)
end
end
else
FireTransitionInternal.call(workitem)
SweepAutomaticTransitions.call(workitem.case)
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/finished_p.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class FinishedP
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
return true if wf_case.finished?
end_place = wf_case.workflow.places.end.first
end_place_token_num = Wf::ApplicationRecord.uncached { wf_case.tokens.where(place: end_place).count }
if end_place_token_num == 0
false
else
free_and_locked_token_num = wf_case.tokens.where(place: end_place).where(state: %i[free locked]).count
raise("The workflow net is misconstructed: Some parallel executions have not finished.") if free_and_locked_token_num > 1
ConsumeToken.call(wf_case, end_place)
unless wf_case.finished?
wf_case.finished!
if started_by_workitem = wf_case.started_by_workitem
Wf::CaseCommand::FinishWorkitem.call(started_by_workitem)
end
end
true
end
end
end
end
================================================
FILE: app/models/wf/case_command/fire_message_transition.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class FireMessageTransition
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
raise("Transition #{workitem.transition.name} is not message triggered") unless workitem.transition.message?
Wf::ApplicationRecord.transaction do
FireTransitionInternal.call(workitem)
SweepAutomaticTransitions.call(workitem.case)
end
end
end
end
================================================
FILE: app/models/wf/case_command/fire_transition_internal.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class FireTransitionInternal
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
if workitem.enabled?
locked_item = nil
elsif workitem.started?
locked_item = workitem
else
raise("can not fire the transition if it is not in state enabled or started.")
end
Wf::ApplicationRecord.transaction do
workitem.update!(finished_at: Time.zone.now, state: :finished)
# TODO: only in?
workitem.transition.arcs.each do |arc|
ConsumeToken.call(workitem.case, arc.place, locked_item)
end
# last arc without guard -> pass
has_passed = false
workitem.transition.arcs.out.order("guards_count DESC").each do |arc|
if workitem.transition.explicit_or_split?
if workitem.pass_guard?(arc, has_passed)
has_passed = true
AddToken.call(workitem.case, arc.place)
end
else
AddToken.call(workitem.case, arc.place)
end
end
workitem.transition.fire_callback.constantize.new(workitem.id).perform_now
end
end
end
end
================================================
FILE: app/models/wf/case_command/lock_token.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class LockToken
prepend SimpleCommand
attr_reader :wf_case, :place, :workitem
def initialize(wf_case, place, workitem)
@wf_case = wf_case
@place = place
@workitem = workitem
end
def call
wf_case.tokens.free.where(place: place).limit(1).update_all(
state: :locked,
locked_at: Time.zone.now,
locked_workitem_id: workitem.id
)
end
end
end
================================================
FILE: app/models/wf/case_command/new.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class New
prepend SimpleCommand
attr_reader :workflow, :target, :started_by
def initialize(workflow, target = nil, started_by = nil)
@workflow = workflow
@target = target
@started_by = started_by
end
def call
wf_case = workflow.cases.create!(targetable: target, started_by_workitem: started_by, state: :created)
wf_case
end
end
end
================================================
FILE: app/models/wf/case_command/release_token.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class ReleaseToken
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
Wf::ApplicationRecord.transaction do
Wf::Token.where(locked_workitem_id: workitem.id).locked.each do |token|
AddToken.call(token.case, token.place)
token.update!(state: :canceled, canceled_at: Time.zone.now)
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/remove_manual_assignment.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class RemoveManualAssignment
prepend SimpleCommand
attr_reader :wf_case, :transition, :party
def initialize(wf_case, transition, party)
@wf_case = wf_case
@transition = transition
@party = party
end
def call
wf_case.case_assignments.where(transition: transition, party: party).find_each(&:destroy)
end
end
end
================================================
FILE: app/models/wf/case_command/remove_workitem_assignment.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class RemoveWorkitemAssignment
prepend SimpleCommand
attr_reader :workitem, :party, :permanent
def initialize(workitem, party, permanent = true)
@workitem = workitem
@party = party
@permanent = permanent
end
def call
return if party.nil?
Wf::ApplicationRecord.transaction do
RemoveManualAssignment.call(workitem.case, workitem.transition, party) if permanent
workitem.workitem_assignments.where(party: party).first&.destroy
workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0
end
end
end
end
================================================
FILE: app/models/wf/case_command/resume.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class Resume
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
raise("Only suspended or canceled cases can be resumed") unless wf_case.suspended? || wf_case.canceled?
wf_case.active!
end
end
end
================================================
FILE: app/models/wf/case_command/set_workitem_assignments.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class SetWorkitemAssignments
prepend SimpleCommand
attr_reader :workitem
def initialize(workitem)
@workitem = workitem
end
def call
Wf::ApplicationRecord.transaction do
has_case_ass = false
workitem.case.case_assignments.where(transition: workitem.transition).find_each do |case_ass|
AddWorkitemAssignment.call(workitem, case_ass.party, false)
has_case_ass = true
end
unless has_case_ass
callback_parties = workitem.transition.assignment_callback.constantize.new.perform(workitem.id)
if callback_parties.present?
callback_parties.each do |party|
AddWorkitemAssignment.call(workitem, party, false)
end
else
workitem.transition.transition_static_assignments.each do |static_assignment|
AddWorkitemAssignment.call(workitem, static_assignment.party, false)
end
end
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/start_case.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class StartCase
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
Wf::ApplicationRecord.transaction do
wf_case.active!
AddToken.call(wf_case, wf_case.workflow.places.start.first)
SweepAutomaticTransitions.call(wf_case)
end
end
end
end
================================================
FILE: app/models/wf/case_command/start_workitem.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class StartWorkitem
prepend SimpleCommand
attr_reader :workitem, :user
def initialize(workitem, user)
@workitem = workitem
@user = user
end
def call
raise("The workitem can not run by user.") unless workitem.real?
raise("The workitem is not in state #{workitem.state}") unless workitem.enabled?
# TODO: holding timeout
Wf::ApplicationRecord.transaction do
workitem.update!(state: :started, holding_user: user)
workitem.transition.arcs.in.each do |arc|
LockToken.call(workitem.case, arc.place, workitem)
end
end
workitem
end
end
end
================================================
FILE: app/models/wf/case_command/suspend.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class Suspend
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
raise("Only active or suspended cases can be canceled") unless wf_case.active?
wf_case.suspended!
end
end
end
================================================
FILE: app/models/wf/case_command/sweep_automatic_transitions.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class SweepAutomaticTransitions
prepend SimpleCommand
attr_reader :wf_case
def initialize(wf_case)
@wf_case = wf_case
end
def call
Wf::ApplicationRecord.transaction do
EnableTransitions.call(wf_case)
done = false
until done
done = true
finished = FinishedP.call(wf_case).result
next if finished
Wf::ApplicationRecord.uncached do
wf_case.workitems.joins(:transition).where(state: :enabled).where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:automatic] }).find_each do |item|
FireTransitionInternal.call(item)
done = false
end
end
EnableTransitions.call(wf_case)
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/sweep_timed_transitions.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class SweepTimedTransitions
prepend SimpleCommand
def call
Wf::ApplicationRecord.transaction do
Wf::Workitem.enabled.where("trigger_time <= ?", Time.zone.now).find_each do |item|
FireTransitionInternal.call(item)
SweepAutomaticTransitions.call(item.case)
end
end
end
end
end
================================================
FILE: app/models/wf/case_command/workitem_action.rb
================================================
# frozen_string_literal: true
module Wf::CaseCommand
class WorkitemAction
prepend SimpleCommand
attr_reader :workitem, :action, :user
def initialize(workitem, user, action = :start)
@workitem = workitem
@action = action
@user = user
end
def call
Wf::ApplicationRecord.transaction do
BeginWorkitemAction.call(workitem, user, action)
EndWorkitemAction.call(workitem, user, action)
end
end
end
end
================================================
FILE: app/models/wf/comment.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_comments
#
# id :integer not null, primary key
# workitem_id :integer
# user_id :string
# body :text
# created_at :datetime not null
# updated_at :datetime not null
#
# frozen_string_literal: true
module Wf
class Comment < ApplicationRecord
belongs_to :workitem
belongs_to :user, class_name: Wf.user_class.to_s
end
end
================================================
FILE: app/models/wf/demo_target.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_demo_targets
#
# id :integer not null, primary key
# name :string
# description :string
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class DemoTarget < ApplicationRecord
has_many :cases, as: :targetable
end
end
================================================
FILE: app/models/wf/entry.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_entries
#
# id :integer not null, primary key
# user_id :string
# workitem_id :integer
# payload :json default("{}")
# created_at :datetime not null
# updated_at :datetime not null
# form_id :integer
#
module Wf
class Entry < ApplicationRecord
belongs_to :form
belongs_to :user, class_name: Wf.user_class.to_s
belongs_to :workitem
has_many :field_values
after_initialize do
self.payload = {} if payload.blank?
end
def json
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
end
def for_mini_racer
field_values.includes(:field).map { |x| [x.field.name, x.value_after_cast] }.to_h
end
def update_payload!
update(payload: json)
end
end
end
================================================
FILE: app/models/wf/field.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_fields
#
# id :integer not null, primary key
# name :string
# form_id :integer
# position :integer default("0")
# field_type :integer default("0")
# field_type_name :string
# default_value :string
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Field < ApplicationRecord
belongs_to :form, touch: true
enum field_type: {
string: 0,
integer: 1,
boolean: 2,
date: 3,
datetime: 4,
decimal: 5,
float: 6,
json: 7,
text: 8,
"string[]": 20,
"integer[]": 21,
"date[]": 23,
"datetime[]": 24,
"decimal[]": 25,
"float[]": 26,
"json[]": 27,
"text[]": 28
}
# TODO: array type
def field_type_for_view
case field_type
when "string"
"text_field"
when "integer"
"number_field"
when "date"
"date_field"
when "datetime"
"datetime_field"
when "boolean"
"check_box"
when "text"
"text_area"
else
"text_field"
end
end
def array?
field_type.to_s.match(/^(\w+)(\[\])?$/)[2] == "[]"
end
def type_for_cast
type = field_type.to_s.match(/^(\w+)(\[\])?$/)[1]
if array?
ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql, array: true)
else
ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql)
end
end
delegate :cast, to: :type_for_cast
end
end
================================================
FILE: app/models/wf/field_value.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_field_values
#
# id :integer not null, primary key
# form_id :integer
# field_id :integer
# value :text
# created_at :datetime not null
# updated_at :datetime not null
# entry_id :integer
#
module Wf
class FieldValue < ApplicationRecord
belongs_to :form
belongs_to :field
belongs_to :entry
def value_after_cast
ov = self[:value]
if field.array? && !ov.is_a?(Array)
v = begin
JSON.parse(ov)
rescue StandardError
[]
end
field.type_for_cast.cast(v)
else
field.type_for_cast.cast(ov)
end
end
def value=(v)
self[:value] = if field.array?
Array(v.as_json)
else
v
end
end
def value
value_after_cast
end
end
end
================================================
FILE: app/models/wf/form.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_forms
#
# id :integer not null, primary key
# name :string
# description :text
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Form < ApplicationRecord
has_many :fields, dependent: :destroy
has_many :entries
end
end
================================================
FILE: app/models/wf/group.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_groups
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Group < ApplicationRecord
has_many :users
include Wf::ActsAsParty
acts_as_party(user: false, party_name: :name)
end
end
================================================
FILE: app/models/wf/guard.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_guards
#
# id :integer not null, primary key
# arc_id :integer
# workflow_id :integer
# fieldable_type :string
# fieldable_id :string
# op :string
# value :string
# exp :string
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Guard < ApplicationRecord
belongs_to :workflow
belongs_to :arc, touch: true, counter_cache: true
belongs_to :fieldable, polymorphic: true, optional: true
before_validation do
self.workflow = arc.workflow
end
validate :validate_exp_and_fieldable
OP = %w[
=
>
<
>=
<=
is_empty
].freeze
def value_after_cast
field = fieldable
fieldable&.cast(value)
end
def pass?(entry, workitem)
if exp
check_exp(entry, workitem)
else
check_fieldable(entry)
end
end
def check_exp(_entry, workitem)
# 1000ms, 200mb
context = MiniRacer::Context.new(timeout: 1000, max_memory: 200_000_000)
context.eval("let target = #{target_hash.to_json};")
context.eval("let workitem = #{workitem.to_json};")
exp_value = context.eval(exp)
yes_or_no?(exp_value, value)
end
def check_fieldable(entry)
fv = entry.field_values.where(field_id: fieldable_id).first
return unless fv
yes_or_no?(fv.value_after_cast, value_after_cast)
end
def yes_or_no?(input_value, setting_value)
if op == "="
input_value == setting_value
elsif op == ">"
input_value > setting_value
elsif op == "<"
input_value < setting_value
elsif op == ">="
input_value >= setting_value
elsif op == "<="
input_value <= setting_value
elsif op == "is_empty"
input_value.blank?
else
false
end
end
def inspect
if exp
%(eval(exp) #{op} #{value})
else
%(#{fieldable&.form&.name}.#{fieldable&.name} #{op} #{value})
end
end
def validate_exp_and_fieldable
if fieldable && exp.present?
errors.add(:exp, "Exp and Fieldable can not be set at the same time.")
return
end
errors.add(:exp, "Must set one of Exp and Fieldable.") unless fieldable || exp.present?
end
end
end
================================================
FILE: app/models/wf/lola.rb
================================================
# frozen_string_literal: true
module Wf
class Lola
attr_reader :end_p, :start_p, :workflow
def initialize(workflow)
@workflow = workflow
@start_p = workflow.places.start.first
@end_p = workflow.places.end.first
generate_lola_file!
end
def to_text
places = workflow.places
transitions = workflow.transitions
places_text = places.map(&:lola_id).join(",")
marking_text = start_p.lola_id
# TODO: with guard
transitions_text = transitions.map do |t|
consume = t.arcs.in.map { |arc| "#{arc.place.lola_id}:1" }.join(",")
produce = t.arcs.out.map { |arc| "#{arc.place.lola_id}:1" }.join(",")
[
"TRANSITION #{t.lola_id}",
"CONSUME #{consume};",
"PRODUCE #{produce};"
].join("\n")
end.join("\n\n")
<<~LOLA
PLACE #{places_text};
MARKING #{marking_text};
#{transitions_text}
LOLA
end
def json_path(bucket)
Rails.root.join("tmp", "#{workflow.id}-#{bucket}.json")
end
def lola_path
Rails.root.join("tmp", "#{workflow.id}-#{workflow.updated_at.to_i}.lola")
end
def generate_lola_file!
File.open(lola_path, "w") { |f| f.write(Wf::Lola.new(workflow).to_text) } unless File.exist?(lola_path)
end
def soundness?
reachability_of_final_marking? && quasiliveness? && !deadlock?
end
def reachability_of_final_marking?
formula = workflow.places.reject { |p| p == end_p }.map { |p| "#{p.lola_id} = 0" }.join(" AND ")
formula += " AND #{end_p.lola_id} >= 1"
formula = "AGEF(#{formula})"
result = run_cmd(formula, "reachability_of_final_marking")
result.dig("analysis", "result")
end
def quasiliveness?
workflow.transitions.all? { |t| !dead_transition?(t) }
end
def deadlock?
formula = "EF (DEADLOCK AND (#{end_p.lola_id} = 0))"
result = run_cmd(formula, "deadlock")
result.dig("analysis", "result")
end
private
def dead_transition?(transition)
formula = "AG NOT FIREABLE (#{transition.lola_id})"
result = run_cmd(formula, "dead_transition_#{transition.id}")
result.dig("analysis", "result")
end
def run_cmd(formula, bucket)
cmd = %(lola #{lola_path} --markinglimit=1000 --timelimit=1 --formula="#{formula}" --json=#{json_path(bucket)})
$stdout.puts cmd
system(cmd)
JSON.parse(File.read(json_path(bucket)))
end
end
end
================================================
FILE: app/models/wf/multiple_instances/all_finish.rb
================================================
# frozen_string_literal: true
module Wf
module MultipleInstances
class AllFinish
def perform(_workitem)
false
end
end
end
end
================================================
FILE: app/models/wf/party.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_parties
#
# id :integer not null, primary key
# partable_type :string
# partable_id :string
# party_name :string
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Party < ApplicationRecord
# TODO: use acts_as_partable for sync group or role or user to party
belongs_to :partable, polymorphic: true
has_many :transition_static_assignments
has_many :workitem_assignments
end
end
================================================
FILE: app/models/wf/place.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_places
#
# id :integer not null, primary key
# workflow_id :integer
# name :string
# description :text
# sort_order :integer default("0")
# place_type :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Place < ApplicationRecord
belongs_to :workflow, touch: true
has_many :arcs
has_many :tokens
enum place_type: {
start: 0,
normal: 1,
end: 2
}
def graph_id
"#{name}/#{id}"
end
def lola_id
"P#{id}"
end
end
end
================================================
FILE: app/models/wf/token.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_tokens
#
# id :integer not null, primary key
# workflow_id :integer
# case_id :integer
# targetable_type :string
# targetable_id :string
# place_id :integer
# state :integer default("0")
# locked_workitem_id :integer
# produced_at :datetime
# locked_at :datetime
# canceled_at :datetime
# consumed_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Token < ApplicationRecord
belongs_to :workflow
belongs_to :case
belongs_to :place
belongs_to :locked_workitem, class_name: "Wf::Workitem", optional: true
enum state: {
free: 0,
locked: 1,
canceled: 2,
consumed: 3
}
end
end
================================================
FILE: app/models/wf/transition.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_transitions
#
# id :integer not null, primary key
# name :string
# description :text
# workflow_id :integer
# sort_order :integer default("0")
# trigger_limit :integer
# trigger_type :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# form_id :integer
# enable_callback :string default("Wf::Callbacks::EnableDefault")
# fire_callback :string default("Wf::Callbacks::FireDefault")
# notification_callback :string default("Wf::Callbacks::NotificationDefault")
# time_callback :string default("Wf::Callbacks::TimeDefault")
# deadline_callback :string default("Wf::Callbacks::DeadlineDefault")
# hold_timeout_callback :string default("Wf::Callbacks::HoldTimeoutDefault")
# assignment_callback :string default("Wf::Callbacks::AssignmentDefault")
# unassignment_callback :string default("Wf::Callbacks::UnassignmentDefault")
# form_type :string default("Wf::Form")
# sub_workflow_id :integer
# multiple_instance :boolean default("false")
# finish_condition :string default("Wf::MultipleInstances::AllFinish")
# dynamic_assign_by_id :integer
#
module Wf
class Transition < ApplicationRecord
belongs_to :workflow, touch: true
has_many :arcs
has_many :transition_static_assignments
has_many :static_parties, through: :transition_static_assignments, source: "party"
has_many :workitems
belongs_to :form, optional: true, polymorphic: true
belongs_to :sub_workflow, optional: true, class_name: "Wf::Workflow"
belongs_to :dynamic_assign_by, optional: true, class_name: "Wf::Transition"
has_many :dynamic_assignments, class_name: "Wf::Transition", foreign_key: "dynamic_assign_by_id"
enum trigger_type: {
user: 0,
automatic: 1,
message: 2,
time: 3
}
validate :validate_trigger_type_and_sub
def is_sub_workflow?
!!sub_workflow_id
end
def explicit_or_split?
arcs.out.sum(:guards_count) >= 1
end
validates :name, presence: true
def validate_trigger_type_and_sub
errors.add(:trigger_type, "sub workflow must have trigger type: automatic, message and time.") if user? && is_sub_workflow?
end
def graph_id
"#{name}/#{id}"
end
def lola_id
"T#{id}"
end
end
end
================================================
FILE: app/models/wf/transition_static_assignment.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_transition_static_assignments
#
# id :integer not null, primary key
# party_id :integer
# transition_id :integer
# workflow_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class TransitionStaticAssignment < ApplicationRecord
belongs_to :workflow
belongs_to :transition
belongs_to :party
validates :party_id, uniqueness: { scope: %i[workflow_id transition_id] }
before_validation do
self.workflow_id = transition&.workflow_id
end
end
end
================================================
FILE: app/models/wf/user.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_users
#
# id :integer not null, primary key
# name :string
# created_at :datetime not null
# updated_at :datetime not null
# group_id :integer
#
module Wf
class User < ApplicationRecord
belongs_to :group, optional: true
include Wf::ActsAsParty
acts_as_party(user: true, party_name: :name)
end
end
================================================
FILE: app/models/wf/workflow.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_workflows
#
# id :integer not null, primary key
# name :string
# description :text
# is_valid :boolean default("false")
# creator_id :string
# error_msg :text
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class Workflow < ApplicationRecord
has_many :places, dependent: :destroy
has_many :transitions, dependent: :destroy
has_many :arcs, dependent: :destroy
has_many :transition_static_assignments
has_many :cases
has_many :workitems
has_many :tokens
validates :name, presence: true
scope :valid, -> { where(is_valid: true) }
after_save do
do_validate!
end
after_touch do
do_validate!
end
# TODO: can from start place to end place
# todo: remove color hex to const.
def do_validate!
msgs = []
start_place = places.start.first
end_place = places.end.first
msgs << "must have start place" if start_place.blank?
msgs << "must have only one start place" if places.start.count > 1
msgs << "must have end place" if end_place.blank?
msgs << "must have only one end place" if places.end.count > 1
msgs << "must not have discrete transition" if transitions.any? { |t| !t.arcs.in.exists? }
if start_place && end_place
rgl = to_rgl
places.each do |p|
msgs << "start place can not reach #{p.name}" unless rgl.path?(start_place.to_gid.to_s, p.to_gid.to_s)
msgs << "#{p.name} can not reach end_place" unless rgl.path?(p.to_gid.to_s, end_place.to_gid.to_s)
end
transitions.each do |t|
msgs << "start place can not reach #{t.name}" unless rgl.path?(start_place.to_gid.to_s, t.to_gid.to_s)
msgs << "#{t.name} can not reach end_place" unless rgl.path?(t.to_gid.to_s, end_place.to_gid.to_s)
end
end
if Wf.use_lola
msgs << "has deadlock" if to_lola.deadlock?
msgs << "has dead transition" unless to_lola.quasiliveness?
msgs << "can not reach to the end place" unless to_lola.reachability_of_final_marking?
end
if msgs.present?
update_columns(is_valid: false, error_msg: msgs.join("\n"))
else
update_columns(is_valid: true, error_msg: "")
end
end
def to_graph(wf_case = nil, base = nil)
fontfamily = "system, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji, Segoe UI Symbol"
fontfamily_monospace = "SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace"
graph = base || GraphViz.new(name, type: :digraph, rankdir: "LR", splines: true, ratio: :auto)
free_token_places = if wf_case
wf_case.tokens.free.map(&:place_id)
else
[]
end
pg_mapping = {}
places.order("place_type ASC").each do |p|
if p.start?
fillcolor = "#ffe7ba"
textcolor = "#fa8c16"
shape = :doublecircle
elsif p.end?
fillcolor = "#dff2ef"
textcolor = "#29c0b1"
shape = :doublecircle
else
fillcolor = "#fbdbe1"
textcolor = "#ff3366"
shape = :circle
end
token_count = free_token_places.count(p.id)
if token_count >= 1
label = "•"
xlabel = nil
fontsize = 25
else
label = p.name
xlabel = nil
end
pg = graph.add_nodes(p.graph_id,
label: label,
xlabel: xlabel,
shape: shape,
fixedsize: true,
style: :filled,
fontname: fontfamily,
fontcolor: textcolor,
fontsize: fontsize,
color: fillcolor,
fillcolor: fillcolor,
href: Wf::Engine.routes.url_helpers.edit_workflow_place_path(self, p))
pg_mapping[p] = pg
end
tg_mapping = {}
transitions.each do |t|
peripheries = if t.multiple_instance?
3
else
1
end
tg = graph.add_nodes(t.graph_id, label: t.name, shape: :box, style: :filled, fillcolor: "#d6ddfa", color: "#d6ddfa",
fontcolor: "#2c50ed", fontname: fontfamily, peripheries: peripheries,
href: Wf::Engine.routes.url_helpers.edit_workflow_transition_path(self, t))
tg_mapping[t] = tg
# NOTICE: if sub_workflow is transition's workflow, then graph will loop infinite, this is valid for workflow definition.
next unless t.is_sub_workflow? && t.sub_workflow != t.workflow
sub_graph = graph.add_graph("cluster#{t.sub_workflow_id}", rankdir: "LR", splines: true, ratio: :auto)
sub_graph[:label] = t.sub_workflow.name
sub_graph[:style] = :dashed
sub_graph[:color] = :lightgrey
# TODO: detect related case for sub workflow.
t.sub_workflow.to_graph(nil, sub_graph)
graph.add_edges(tg, t.sub_workflow.places.start.first.graph_id, style: :dashed, dir: :both)
end
arcs.order("direction desc").each do |arc|
label = if arc.guards_count > 0
arc.guards.map(&:inspect).join(" & ")
else
""
end
if arc.in?
graph.add_edges(
pg_mapping[arc.place],
tg_mapping[arc.transition],
label: label,
weight: 1,
labelfloat: false,
labelfontcolor: :red,
arrowhead: :vee,
fontsize: 10,
color: "#53585c",
fontcolor: "#53585c",
fontname: fontfamily_monospace,
href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)
)
else
graph.add_edges(
tg_mapping[arc.transition],
pg_mapping[arc.place],
label: label,
weight: 1,
labelfloat: false,
labelfontcolor: :red,
arrowhead: :vee,
fontsize: 10,
color: "#53585c",
fontcolor: "#53585c",
fontname: fontfamily_monospace,
href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)
)
end
end
graph
end
def render_graph(wf_case = nil)
graph = to_graph(wf_case)
path = Rails.root.join("tmp", "#{id}.svg")
graph.output(svg: path)
File.read(path)
end
def to_lola
@lola ||= Wf::Lola.new(self)
end
def to_rgl
graph = RGL::DirectedAdjacencyGraph.new
places.order("place_type ASC").each do |p|
graph.add_vertex(p.to_gid.to_s)
end
transitions.each do |t|
graph.add_vertex(t.to_gid.to_s)
end
arcs.order("direction desc").each do |arc|
if arc.in?
graph.add_edge(arc.place.to_gid.to_s, arc.transition.to_gid.to_s)
else
graph.add_edge(arc.transition.to_gid.to_s, arc.place.to_gid.to_s)
end
end
graph
end
end
end
================================================
FILE: app/models/wf/workitem.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_workitems
#
# id :integer not null, primary key
# case_id :integer
# workflow_id :integer
# transition_id :integer
# state :integer default("0")
# enabled_at :datetime
# started_at :datetime
# canceled_at :datetime
# finished_at :datetime
# overridden_at :datetime
# deadline :datetime
# created_at :datetime not null
# updated_at :datetime not null
# trigger_time :datetime
# holding_user_id :string
# children_count :integer default("0")
# children_finished_count :integer default("0")
# forked :boolean default("false")
# parent_id :integer
#
module Wf
class Workitem < ApplicationRecord
belongs_to :workflow
belongs_to :transition
belongs_to :case
belongs_to :parent, class_name: "Wf::Workitem", optional: true, counter_cache: :children_count
belongs_to :holding_user, foreign_key: :holding_user_id, class_name: Wf.user_class.to_s, optional: true
has_many :workitem_assignments
has_many :parties, through: :workitem_assignments, source: "party"
has_many :comments
has_many :entries, class_name: Wf.entry_class.to_s
has_one :started_case, foreign_key: :started_by_workitem_id, class_name: "Wf::Case"
has_many :children, foreign_key: :parent_id, class_name: "Wf::Workitem"
enum state: {
enabled: 0,
started: 1,
canceled: 2,
finished: 3,
overridden: 4
}
def self.todo(wf_current_user)
current_party_ids = [
wf_current_user,
Wf.org_classes.map { |org, _org_class| wf_current_user&.public_send(org) }
].flatten.map { |x| x&.party&.id }.compact
Wf::Workitem.where(forked: false).joins(:workitem_assignments).where(Wf::WorkitemAssignment.table_name => { party_id: current_party_ids })
end
def self.doing(wf_current_user)
where(holding_user: wf_current_user).where(state: %i[started enabled])
end
def self.done(wf_current_user)
where(holding_user: wf_current_user).where(state: [:finished])
end
def for_mini_racer
attr = attributes
attr = attr.merge(holding_user: holding_user&.attributes || {})
attr = attr.merge(form: entries.to_a.first&.for_mini_racer || {})
children_attrs = if forked?
[]
else
children.includes(:holding_user, entries: :field_values).map(&:for_mini_racer)
end
attr = attr.merge(children: children_attrs)
attr
end
def parent?
!forked
end
def name
"Workitem -> #{id}"
end
def pass_guard?(arc, has_passed = false)
if arc.guards_count == 0
!has_passed
else
entry = entries.where(user: holding_user).first
arc.guards.all? { |guard| guard.pass?(entry, self) }
end
end
def real?
return false if transition.multiple_instance? && parent_id.nil?
return false if transition.sub_workflow_id.present?
true
end
def started_by?(user)
real? && enabled? && owned_by?(user)
end
def finished_by?(user)
real? && started? && owned_by?(user) && holding_user == user
end
def owned_by?(user)
Wf::Party.joins(workitem_assignments: { workitem: %i[transition case] })
.where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:user] })
.where(Wf::Case.table_name => { state: Wf::Case.states[:active] })
.where(Wf::Workitem.table_name => { state: Wf::Workitem.states.values_at(:started, :enabled) })
.where(Wf::Workitem.table_name => { id: id }).map do |party|
party.partable.users.to_a
end.flatten.include?(user)
end
end
end
================================================
FILE: app/models/wf/workitem_assignment.rb
================================================
# frozen_string_literal: true
# == Schema Information
#
# Table name: wf_workitem_assignments
#
# id :integer not null, primary key
# party_id :integer
# workitem_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
module Wf
class WorkitemAssignment < ApplicationRecord
belongs_to :party
belongs_to :workitem
end
end
================================================
FILE: app/views/layouts/wf/_alert.html.erb
================================================
<% return if alert.blank? %>
<div id="alert" class="alert alert-warning">
<p>
<svg viewBox="64 64 896 896" focusable="false" class="" data-icon="exclamation-circle" width="1em" height="1em" fill="#fa4a4a" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path></svg> <%= alert %></p>
</div>
================================================
FILE: app/views/layouts/wf/_footer.html.erb
================================================
<footer class="footer">
<div class="container">
<p>
<strong>
<a href="https://github.com/hooopo/petri_flow" target="_blank" rel="nofollow">Petri Flow</a>
</strong>
crafted by <a href="https://github.com/hooopo" target="_blank" rel="nofollow">Hooopo</a>.
The source code is licensed under <a href="http://opensource.org/licenses/mit-license.php">MIT license</a>.
</p>
</div>
</footer>
================================================
FILE: app/views/layouts/wf/_nav.html.erb
================================================
<nav class="navbar navbar-petri" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<%= link_to "👨💻Petri Flow", root_path, class: "navbar-item" %>
</div>
<div class="navbar-end">
<%= link_to wf_current_user&.name, workitems_path %>
<%= link_to 'Forms', forms_path, class: "navbar-item #{"is-active" if controller_name == "forms"}" %>
<%= link_to 'Workflows', workflows_path, class: "navbar-item #{"is-active" if controller_name.start_with?("workflows")}" %>
</div>
</div>
</nav>
================================================
FILE: app/views/layouts/wf/_notice.html.erb
================================================
<% return if notice.blank? %>
<div id="notice" class="alert alert-success">
<p>
<svg viewBox="64 64 896 896" focusable="false" class="" data-icon="check-circle" width="1em" height="1em" fill="#00d192" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path></svg> <%= notice %></p>
</div>
================================================
FILE: app/views/layouts/wf/application.html.erb
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Petri Flow</title>
<%= csrf_meta_tags %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<%= stylesheet_link_tag "wf/application", media: "all", "data-turbolinks-track": "reload" %>
<%= javascript_include_tag "wf/application", "data-turbolinks-track": "reload" %>
</head>
<body>
<%= render "layouts/wf/nav" %>
<main class="container">
<%= render "layouts/wf/notice" %>
<%= render "layouts/wf/alert" %>
<!--
<nav aria-label="breadcrumb">
<ol class='breadcrumb'>
<% breadcrumb_trail do |crumb| %>
<li class="breadcrumb-item <%= crumb.current? ? 'active' : '' %>">
<%= link_to crumb.name, crumb.url, (crumb.current? ? {'aria-current' => 'page'} : {}) %>
</li>
<% end %>
</ol>
</nav>
-->
<%= content_for?(:content) ? yield(:content) : yield %>
</main>
<%= render "layouts/wf/footer" %>
</body>
</html>
================================================
FILE: app/views/wf/arcs/_form.html.erb
================================================
<%= form_with(model: arc, url: [@workflow, @arc], local: true) do |f| %>
<% if arc.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(arc.errors.count, "error") %> prohibited this arc from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% arc.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :place, class: "label" %>
<%= f.select :place_id, @workflow.places.map{|x| [x.name, x.id]}, {}, class: "form-control custom-select", placeholder: "Place" %>
</div>
<div class="form-group">
<%= f.label :transition, class: "label" %>
<%= f.select :transition_id, @workflow.transitions.map{|x| [x.name, x.id]}, {}, class: "custom-select", placeholder: "Transition" %>
</div>
<div class="form-group">
<div id="notice" class="alert alert-success">
Direction is base on transition, in: P->T, out: T->P.
</div>
<%= f.label :direction, class: "label" %>
<%= f.select :direction, Wf::Arc.directions.keys, {}, class: "form-control custom-select", placeholder: "Direction" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/arcs/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, arc: @arc %>
</div>
================================================
FILE: app/views/wf/arcs/new.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, arc: @arc %>
</div>
================================================
FILE: app/views/wf/arcs/show.html.erb
================================================
<div class="card card-body">
<div class="float-right">
<%= link_to 'Delete Arc', workflow_path(@workflow, @arc), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger' %>
<%= link_to 'Edit Arc', edit_workflow_arc_path(@workflow, @arc), class: 'btn btn-primary' %>
<% if @arc.out? %>
<%= link_to 'Create Guards', new_arc_guard_path(@arc), class: 'btn btn-primary' %>
<% end %>
</div>
<div>
<h2>Arc Detail</h2>
<table class="table table-view">
<tbody>
<tr>
<th scope="row">ID</th>
<td><%= @arc.id %></td>
</tr>
<tr>
<th scope="row">Name</th>
<td><%= @arc.name %></td>
</tr>
<tr>
<th scope="row">Transition</th>
<td><%= @arc.transition.name %></td>
</tr>
<tr>
<th scope="row">Place</th>
<td><%= @arc.place.name %></td>
</tr>
<tr>
<th scope="row">Guards</th>
<td><%= @arc.guards.map {|x| x.inspect }.join(" & ") %></td>
</tr>
<tr>
<th scope="row">Direction</th>
<td><%= @arc.direction %></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Guards</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Field</th>
<th scope="col">Op</th>
<th scope="col">Value</th>
<th scope="col">Exp</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<% @arc.guards.each do |guard| %>
<tr>
<td><%= guard.id %></td>
<td><%= guard.fieldable&.form&.name %>/<%= guard.fieldable&.name %></td>
<td><%= guard.op %></td>
<td><%= guard.value %></td>
<td>
<pre>
<%= guard.exp %>
</pre>
</td>
<td><%= link_to 'Edit Guard', edit_arc_guard_path(@arc, guard), class: 'btn btn-sm btn-info' %></td>
<td><%= link_to 'Delete Guard', [@arc, guard], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-info' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
================================================
FILE: app/views/wf/cases/_form.html.erb
================================================
<%= form_with(model: wf_case, url: [@workflow, @wf_case], local: true) do |f| %>
<% if wf_case.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(wf_case.errors.count, "error") %> prohibited this wf_case from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% wf_case.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :targetable, class: "label" %>
<%= f.select :targetable, options_for_select(Wf::DemoTarget.all.map {|x| [x.name, x.to_global_id]} || []), {include_blank: true}, class: "form-control custom-select", placeholder: "targetable" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/cases/index.html.erb
================================================
<div class="card card-body">
<div class="float-left"><h2>Cases</h2></div>
<div class="float-right">
<%= link_to 'New Case', new_workflow_case_path(@workflow), class: 'btn btn-primary' %>
</div>
<div>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">State</th>
<th scope="col">Created At</th>
<th scope="col">Targetable Type</th>
<th scope="col">Targetable ID</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<% @cases.each do |wf_case| %>
<tr>
<td><%= wf_case.id %></td>
<td><%= wf_case.state %></td>
<td><%= wf_case.created_at %></td>
<td><%= wf_case.targetable_type %></td>
<td><%= wf_case.targetable_id %></td>
<td>
<%= link_to "Run", workflow_case_path(@workflow, wf_case), class: 'btn btn-success btn-sm' %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<%= paginate @cases, theme: 'twitter-bootstrap-4' %>
</div>
</div>
================================================
FILE: app/views/wf/cases/new.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, wf_case: @wf_case %>
</div>
================================================
FILE: app/views/wf/cases/show.html.erb
================================================
<div class="card card-body">
<div>
<h2>Case Detail</h2>
<table class="table table-view">
<tbody>
<tr>
<th scope="row">ID</th>
<td><%= @wf_case.id %></td>
</tr>
<tr>
<th scope="row">Workflow</th>
<td><%= link_to @wf_case.workflow.name, workflow_path(@wf_case.workflow) %></td>
</tr>
<tr>
<th scope="row">State</th>
<td><%= @wf_case.state %></td>
</tr>
<tr>
<th scope="row">Targetable Type</th>
<td><%= @wf_case.targetable_type %></td>
</tr>
<tr>
<th scope="row">Targetable ID</th>
<td><%= @wf_case.targetable_id %></td>
</tr>
<tr>
<th scope="row">Created At</th>
<td><%= @wf_case.created_at %></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Case Graph</h2>
<div class="card">
<div class="card-body">
<%=raw @wf_case.workflow.render_graph(@wf_case) %>
</div>
</div>
</div>
<div>
<h2>Tokens</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Place</th>
<th scope="col">State</th>
<th scope="col">Locked Workitem</th>
<th scope="col">Produced At</th>
<th scope="col">Locked At</th>
<th scope="col">Canceled At</th>
<th scope="col">Consumed At</th>
<th scope="col">Created At</th>
</tr>
</thead>
<tbody>
<% @wf_case.tokens.each do |token| %>
<tr>
<td><%= token.id %></td>
<td><%= link_to token.place.name, workflow_place_path(token.workflow, token.place) %> </td>
<td><%= token.state %></td>
<td><%= token.locked_workitem_id %></td>
<td><%= token.produced_at %></td>
<td><%= token.locked_at %></td>
<td><%= token.canceled_at %></td>
<td><%= token.consumed_at %></td>
<td><%= token.created_at %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<h2>Workitems</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Transition</th>
<th scope="col">State</th>
<th scope="col">Holding User</th>
<th scope="col">Started At</th>
<th scope="col">Enabled At</th>
<th scope="col">Canceled At</th>
<th scope="col">Finished At</th>
<th scope="col">Overridden At</th>
<th scope="col">Deadline</th>
<th scope="col">Detail</th>
</tr>
</thead>
<tbody>
<% @wf_case.workitems.each do |workitem| %>
<tr>
<td><%= link_to workitem.id, workitem_path(workitem) %></td>
<td>
<%= link_to workitem.transition.name, workflow_transition_path(workitem.workflow, workitem.transition) %> </td>
<td><%= workitem.state %></td>
<td><%= workitem.holding_user_id %></td>
<td><%= workitem.started_at %></td>
<td><%= workitem.enabled_at %></td>
<td><%= workitem.canceled_at %></td>
<td><%= workitem.finished_at %></td>
<td><%= workitem.overridden_at %></td>
<td><%= workitem.deadline %></td>
<td><%= link_to "Run", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
================================================
FILE: app/views/wf/comments/new.html.erb
================================================
<div class="card card-body">
<%= form_with(model: @comment, url: workitem_comments_path(@workitem), local: true) do |f| %>
<% if @comment.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(@comment.errors.count, "error") %> prohibited this comment from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% @comment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :body, class: "label" %>
<%= f.text_area :body, class: "form-control", rows: 3, placeholder: "comment" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", value: "Comment", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
</div>
================================================
FILE: app/views/wf/fields/_form.html.erb
================================================
<%= form_with(model: field, url: [@form, field], local: true) do |f| %>
<% if field.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(field.errors.count, "error") %> prohibited this field from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% field.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :name, class: "label" %>
<%= f.text_field :name, class: "form-control", fieldholder: "Name" %>
</div>
<div class="form-group">
<%= f.label :position, class: "label" %>
<%= f.text_field :position, class: "form-control", fieldholder: "Position" %>
</div>
<div class="form-group">
<%= f.label :default_value, class: "label" %>
<%= f.text_field :default_value, class: "form-control", fieldholder: "Default Value" %>
</div>
<div class="form-group">
<%= f.label :field_type, class: "label" %>
<%= f.select :field_type, Wf::Field.field_types.keys, {}, class: "form-control custom-select", fieldholder: "Field Type" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/fields/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", form: @form, field: @field %>
</div>
================================================
FILE: app/views/wf/fields/new.html.erb
================================================
<div class="card card-body">
<%= render "form", form: @form, field: @field %>
</div>
================================================
FILE: app/views/wf/forms/_form.html.erb
================================================
<%= form_with(model: form, local: true) do |f| %>
<% if form.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(form.errors.count, "error") %> prohibited this form from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :name, class: "label" %>
<%= f.text_field :name, class: "form-control", placeholder: "Name" %>
</div>
<div class="form-group">
<%= f.label :description, class: "label" %>
<%= f.text_area :description, class: "form-control", placeholder: "Description" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/forms/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", form: @form %>
</div>
================================================
FILE: app/views/wf/forms/index.html.erb
================================================
<div class="card card-body">
<div class="d-flex justify-content-between">
<div><h2>Forms</h2></div>
<div><%= link_to '+ New Form', new_form_path, class: 'btn btn-primary' %></div>
</div>
<div>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% @forms.each do |form| %>
<tr>
<td><%= form.id %></td>
<td><%= link_to form.name, form_path(form) %></td>
<td><%= form.description %></td>
<td class="text-right">
<%= link_to 'Delete Form', form_path(form), method: :delete, data: {confirm: 'confirm?'}, class: 'btn-link btn btn-sm text-danger' %>
<%= link_to 'Create Field', new_form_field_path(form), class: 'btn btn-link btn-sm' %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<%= paginate @forms, theme: 'twitter-bootstrap-4' %>
</div>
</div>
================================================
FILE: app/views/wf/forms/new.html.erb
================================================
<div class="card card-body">
<%= render "form", form: @form %>
</div>
================================================
FILE: app/views/wf/forms/show.html.erb
================================================
<div class="card card-body">
<div class="d-flex justify-content-end">
<%= link_to 'Delete Form', form_path(@form), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger mr-2' %>
<%= link_to 'Edit Form', edit_form_path(@form), class: 'btn btn-light mr-2' %>
<%= link_to 'Create Fields', new_form_field_path(@form), class: 'btn btn-light' %>
</div>
<div>
<h2>Form Detail</h2>
<table class="table table-view">
<tbody>
<tr>
<th scope="row">ID</th>
<td><%= @form.id %></td>
</tr>
<tr>
<th scope="row">Name</th>
<td><%= @form.name %></td>
</tr>
<tr>
<th scope="row">Description</th>
<td><%= @form.description %></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Fields</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Position</th>
<th scope="col">Field Type</th>
<th scope="col">Default Value</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% @form.fields.each do |field| %>
<tr>
<td><%= field.id %></td>
<td><%= field.name %></td>
<td><%= field.position %></td>
<td><%= field.field_type %></td>
<td><%= field.default_value %></td>
<td class="text-right"><%= link_to 'Edit Field', edit_form_field_path(@form, field), class: 'btn btn-sm btn-link mr-2' %>
<%= link_to 'Delete Field', [@form, field], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
================================================
FILE: app/views/wf/guards/_form.html.erb
================================================
<%= form_with(model: guard, url: [@arc, @guard], local: true) do |f| %>
<% if guard.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(guard.errors.count, "error") %> prohibited this guard from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% guard.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :fieldable, class: "label" %>
<%= f.select :fieldable, options_for_select(@arc.transition.form&.fields&.map {|x| [x.name, x.to_global_id]} || [], selected: f.object&.fieldable&.to_global_id), {include_blank: "select a field"}, class: "form-control custom-select", placeholder: "fieldable" %>
</div>
<div class="form-group">
<%= f.label :exp, class: "label" %> <small>JavaScript with build-in variable <span class="badge badge-danger">workitem</span>, <span class="badge badge-danger">target</span>.</small>
<%= f.text_area :exp, class: "form-control", placeholder: "Exp", rows: 8 %>
</div>
<div class="form-group">
<%= f.label :op, class: "label" %>
<%= f.select :op, Wf::Guard::OP, {}, class: "form-control custom-select", placeholder: "Op" %>
</div>
<div class="form-group">
<%= f.label :value, class: "label" %>
<%= f.text_field :value, class: "form-control", placeholder: "Value" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/guards/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", arc: @arc, guard: @guard %>
</div>
================================================
FILE: app/views/wf/guards/new.html.erb
================================================
<div class="card card-body">
<%= render "form", arc: @arc, guard: @guard %>
</div>
================================================
FILE: app/views/wf/places/_form.html.erb
================================================
<%= form_with(model: place, url: [@workflow, @place], local: true) do |f| %>
<% if place.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(place.errors.count, "error") %> prohibited this place from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% place.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :name, class: "label" %>
<%= f.text_field :name, class: "form-control", placeholder: "Name" %>
</div>
<div class="form-group">
<%= f.label :description, class: "label" %>
<%= f.text_area :description, class: "form-control", placeholder: "Description" %>
</div>
<div class="form-group">
<%= f.label :sort_order, class: "label" %>
<%= f.text_field :sort_order, class: "form-control", placeholder: "Sort Order" %>
</div>
<div class="form-group">
<%= f.label :place_type, class: "label" %>
<%= f.select :place_type, Wf::Place.place_types.keys, {}, class: "form-control custom-select", placeholder: "Place Type" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/places/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, place: @place %>
</div>
================================================
FILE: app/views/wf/places/new.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, place: @place %>
</div>
================================================
FILE: app/views/wf/static_assignments/_form.html.erb
================================================
<%= form_with(model: static_assignment, url: transition_static_assignments_path(static_assignment.transition), local: true) do |f| %>
<% if static_assignment.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(static_assignment.errors.count, "error") %> prohibited this static_assignment from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% static_assignment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :party, class: "label" %>
<%= f.select :party_id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: "form-control custom-select", placeholder: "fieldable" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", value: 'Create Static Assignment', data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/static_assignments/new.html.erb
================================================
<div class="card card-body">
<%= render "form", static_assignment: @static_assignment %>
</div>
================================================
FILE: app/views/wf/transitions/_form.html.erb
================================================
<%= form_with(model: transition, url: [@workflow, @transition], local: true) do |f| %>
<% if transition.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(transition.errors.count, "error") %> prohibited this transition from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% transition.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="card">
<h5 class="card-header">Basic Information</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :name, class: "label" %>
<%= f.text_field :name, class: "form-control", placeholder: "Name" %>
</div>
<div class="form-group">
<%= f.label :description, class: "label" %>
<%= f.text_area :description, class: "form-control", placeholder: "Description" %>
</div>
<div class="form-group">
<%= f.label :trigger_limit, class: "label" %>
<%= f.text_field :trigger_limit, class: "form-control", placeholder: "Trigger Limit" %>
</div>
<div class="form-group">
<%= f.label :trigger_type, class: "label" %>
<%= f.select :trigger_type, Wf::Transition.trigger_types.keys, {}, class: "form-control custom-select", placeholder: "Trigger Type" %>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">Sub Workflow</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :sub_workflow, class: "label" %>
<%= f.select :sub_workflow_id, options_for_select(Wf::Workflow.valid.all.map{|x| [x.name, x.id]} || []), {include_blank: 'Start a sub workflow?'}, class: "form-control custom-select", placeholder: "Sub Workflow" %>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">Dynimac Form</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :form, class: "label" %>
<%= f.select :form, options_for_select(Wf.form_class.constantize.all.map{|x| [x.name, x.to_global_id]} || [], selected: f.object&.form&.to_global_id), {include_blank: 'user can input from custom form?'}, class: "form-control custom-select", placeholder: "Form" %>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">Dynamic Assign By</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :dynamic_assign_by, class: "label" %>
<%= f.select :dynamic_assign_by_id, options_for_select(@workflow.transitions.where("id != ?", @transition.id).all.map{|x| [x.name, x.id]} || []), {include_blank: 'What other transition you want to do the assignment for this transition?'}, class: "form-control custom-select" %>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">Callbacks</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :enable_callback, class: "label" %>
<%= f.select :enable_callback, Wf.enable_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :fire_callback, class: "label" %>
<%= f.select :fire_callback, Wf.fire_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :deadline_callback, class: "label" %>
<%= f.select :deadline_callback, Wf.deadline_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :time_callback, class: "label" %>
<%= f.select :time_callback, Wf.time_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :hold_timeout_callback, class: "label" %>
<%= f.select :hold_timeout_callback, Wf.hold_timeout_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :assignment_callback, class: "label" %>
<%= f.select :assignment_callback, Wf.assignment_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :unassignment_callback, class: "label" %>
<%= f.select :unassignment_callback, Wf.unassignment_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
<div class="form-group">
<%= f.label :notification_callback, class: "label" %>
<%= f.select :notification_callback, Wf.notification_callbacks, {}, class: "form-control custom-select", placeholder: "Callback" %>
</div>
</div>
</div>
<div class="card">
<h5 class="card-header">Multiple Instances</h5>
<div class="card-body">
<div class="form-group">
<%= f.label :multiple_instance, class: "label" %>
<%= f.check_box :multiple_instance %>
</div>
<div class="form-group">
<%= f.label :finish_condition, class: "label" %>
<%= f.select :finish_condition, Wf.finish_conditions, {}, class: "form-control custom-select", placeholder: "Finish_condition" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/transitions/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, transition: @transition %>
</div>
================================================
FILE: app/views/wf/transitions/new.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow, transition: @transition %>
</div>
================================================
FILE: app/views/wf/transitions/show.html.erb
================================================
<div class="card card-body">
<div class="float-right">
<%= link_to 'Edit Transition', edit_workflow_transition_path(@workflow, @transition), class: 'btn btn-primary' %>
<%= link_to 'Create Static Assignments', new_transition_static_assignment_path(@transition), class: 'btn btn-primary' %>
</div>
<div>
<h2>Transition Detail</h2>
<table class="table table-view">
<tbody>
<tr>
<th scope="row">ID</th>
<td><%= @transition.id %></td>
</tr>
<tr>
<th scope="row">Name</th>
<td><%= @transition.name %></td>
</tr>
<tr>
<th scope="row">Trigger Limit</th>
<td><%= @transition.trigger_limit %></td>
</tr>
<tr>
<th scope="row">Trigger Type</th>
<td><%= @transition.trigger_type %></td>
</tr>
<tr>
<th>Form</th>
<td>
<% if @transition.form %>
<%= link_to @transition.form.name, form_path(@transition.form) %>
<% else %>
No Form
<% end %>
</td>
</tr>
<tr>
<th>Sub Workflow</th>
<td>
<% if @transition.sub_workflow %>
<%= link_to @transition.sub_workflow.name, workflow_path(@transition.sub_workflow_id) %>
<% else %>
No
<% end %>
</td>
</tr>
<tr>
<th>Dynamic Assign By</th>
<td>
<% if @transition.dynamic_assign_by %>
<%= link_to @transition.dynamic_assign_by.name, workflow_transition_path(@workflow, @transition.dynamic_assign_by_id) %>
<% else %>
No
<% end %>
</td>
</tr>
<tr>
<th scope="row">Multiple Instance?</th>
<td><%= @transition.multiple_instance %></td>
</tr>
<tr>
<th scope="row">Finish Condition</th>
<td><%= @transition.finish_condition %></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Static Assignments</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Party Name</th>
<th scope="col">Party Type</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<% @transition.transition_static_assignments.each do |assign| %>
<tr>
<td><%= assign.id %></td>
<td><%= assign.party.party_name %></td>
<td><%= assign.party.partable_type %></td>
<td><%= link_to 'Delete', transition_static_assignment_path(@transition, assign), remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-info' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
================================================
FILE: app/views/wf/workflows/_form.html.erb
================================================
<%= form_with(model: workflow, local: true) do |f| %>
<% if workflow.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(workflow.errors.count, "error") %> prohibited this workflow from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% workflow.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :name, class: "label" %>
<%= f.text_field :name, class: "form-control", placeholder: "Name" %>
</div>
<div class="form-group">
<%= f.label :description, class: "label" %>
<%= f.text_area :description, class: "form-control", placeholder: "Description" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
================================================
FILE: app/views/wf/workflows/edit.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow %>
</div>
================================================
FILE: app/views/wf/workflows/index.html.erb
================================================
<div class="card card-body">
<div class="d-flex justify-content-between">
<div><h2>Workflows</h2></div>
<div>
<%= link_to 'New Workflow', new_workflow_path, class: 'btn btn-primary' %>
</div>
</div>
<div>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Is Valid?</th>
<th scope="col">Error Msg</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% @workflows.each do |workflow| %>
<tr>
<td><%= workflow.id %></td>
<td><%= link_to workflow.name, workflow_path(workflow) %></td>
<td><%= workflow.description %></td>
<td><%= workflow.is_valid? %></td>
<td>
<pre>
<%= workflow.error_msg %>
</pre>
</td>
<td class="text-right">
<%= link_to 'Delete Workflow', workflow_path(workflow), remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-link btn-sm text-danger' %>
<%= link_to 'Create Transition', new_workflow_transition_path(workflow), class: 'btn btn-link btn-sm' %>
<%= link_to 'Create Place', new_workflow_place_path(workflow), class: 'btn btn-link btn-sm' %>
<%= link_to 'Create Arc', new_workflow_arc_path(workflow), class: 'btn btn-link btn-sm' %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<%= paginate @workflows, theme: 'twitter-bootstrap-4' %>
</div>
</div>
================================================
FILE: app/views/wf/workflows/new.html.erb
================================================
<div class="card card-body">
<%= render "form", workflow: @workflow %>
</div>
================================================
FILE: app/views/wf/workflows/show.html.erb
================================================
<div class="d-flex justify-content-end">
<% if @workflow.is_valid? %>
<%= link_to 'New Case', new_workflow_case_path(@workflow), class: 'btn btn-primary mr-2' %>
<% end %>
<%= link_to 'Delete Workflow', workflow_path(@workflow), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger mr-2' %>
<%= link_to 'Edit Workflow', edit_workflow_path(@workflow), class: 'btn btn-light mr-2' %>
<%= link_to 'Create Transitions', new_workflow_transition_path(@workflow), class: 'btn btn-light mr-2' %>
<%= link_to 'Create Places', new_workflow_place_path(@workflow), class: 'btn btn-light mr-2' %>
<%= link_to 'Create Arcs', new_workflow_arc_path(@workflow), class: 'btn btn-light ' %>
</div>
<div class="card card-body">
<h2 class="">Workflow Detail</h2>
<table class="table table-view">
<tbody>
<tr>
<th scope="row">ID</th>
<td><%= @workflow.id %></td>
</tr>
<tr>
<th scope="row">Name</th>
<td><%= @workflow.name %></td>
</tr>
<tr>
<th scope="row">Description</th>
<td><%= @workflow.description %></td>
</tr>
<tr>
<th scope="row">Is Valid?</th>
<td><code><%= @workflow.is_valid? %></code></td>
</tr>
<tr>
<th scope="row">Error Msg</th>
<td>
<pre>
<%= @workflow.error_msg %>
</pre>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card card-body">
<h2>Graph Editor</h2>
<div class="card">
<div class="card-body">
<%=raw @workflow.render_graph %>
</div>
</div>
</div>
<div class="card card-body">
<h2 class="">Cases</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">All</th>
<th scope="col">Created</th>
<th scope="col">Active</th>
<th scope="col">Suspended</th>
<th scope="col">Canceled</th>
<th scope="col">Finished</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= link_to @workflow.cases.count, workflow_cases_path(@workflow) %></td>
<td><%= link_to @workflow.cases.created.count, workflow_cases_path(@workflow, state: :created) %></td>
<td><%= link_to @workflow.cases.active.count, workflow_cases_path(@workflow, state: :active) %></td>
<td><%= link_to @workflow.cases.suspended.count, workflow_cases_path(@workflow, state: :suspended) %></td>
<td><%= link_to @workflow.cases.canceled.count, workflow_cases_path(@workflow, state: :canceled) %></td>
<td><%= link_to @workflow.cases.finished.count, workflow_cases_path(@workflow, state: :finished) %></td>
</tr>
</tbody>
</table>
</div>
<div class="card card-body">
<h2>Places</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Place Type</th>
<th scope="col">Sort Order</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% @workflow.places.each do |place| %>
<tr>
<td><%= place.id %></td>
<td><%= place.name %></td>
<td><%= place.description %></td>
<td><%= place.place_type %></td>
<td><%= place.sort_order %></td>
<td class="text-right">
<%= link_to 'Edit Place', edit_workflow_place_path(@workflow, place), class: 'btn btn-sm btn-link mr-2' %>
<%= link_to 'Delete Place', [@workflow, place], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="card card-body">
<h2>Transitions</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Trigger Limit</th>
<th scope="col">Trigger Type</th>
<th scope="col">Sort Order</th>
<th scope="col">Custom Form</th>
<th scope="col">Sub Workflow</th>
<th scope='col'> </th>
</tr>
</thead>
<tbody>
<% @workflow.transitions.each do |transition| %>
<tr>
<td><%= transition.id %></td>
<td><%= link_to transition.name, workflow_transition_path(@workflow, transition) %></td>
<td><%= transition.description %></td>
<td><%= transition.trigger_limit %></td>
<td><%= transition.trigger_type %></td>
<td><%= transition.sort_order %></td>
<td>
<% if transition.form %>
<%= link_to transition.form.name, form_path(transition.form) %>
<% else %>
No Form
<% end %>
</td>
<td>
<% if transition.sub_workflow %>
<%= link_to transition.sub_workflow.name, workflow_path(transition.sub_workflow_id) %>
<% else %>
No
<% end %>
</td>
<td><%= link_to 'Edit Transition', edit_workflow_transition_path(@workflow, transition), class: 'btn btn-sm btn-link mr-2' %>
<%= link_to 'Delete Transition', [@workflow, transition], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="card card-body">
<h2>Arcs</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Direction</th>
<th scope="col">Place</th>
<th scope="col">Transition</th>
<th scope="col">Arc Type</th>
<th scope="col">Guards</th>
<th scope='col'> </th>
</tr>
</thead>
<tbody>
<% @workflow.arcs.includes(:transition, :place).each do |arc| %>
<tr>
<td><%= link_to arc.id, workflow_arc_path(@workflow, arc) %></td>
<td><%= link_to arc.name, workflow_arc_path(@workflow, arc) %></td>
<td><%= arc.direction %></td>
<td><%= arc.place&.name %></td>
<td><%= arc.transition&.name %></td>
<td><code><%= arc.guards.map(&:inspect).join(" & ") %></code></td>
<td>
<% if arc.out? %>
<%= link_to 'Add Gruards', new_arc_guard_path(arc), class: 'btn btn-sm btn-outline-primary mr-2' %>
<% end %>
</td>
<td class="text-right">
<%= link_to 'Edit Arc', edit_workflow_arc_path(@workflow, arc), class: 'btn btn-sm btn-link mr-2' %>
<%= link_to 'Delete Arc', [@workflow, arc], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
================================================
FILE: app/views/wf/workitem_assignments/new.html.erb
================================================
<div class="card card-body">
<%= form_with(model: @workitem_assignment, url: workitem_workitem_assignments_path(@workitem), local: true) do |f| %>
<% if @workitem_assignment.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(@workitem_assignment.errors.count, "error") %> prohibited this workitem_assignment from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% @workitem_assignment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<div class="form-group">
<%= f.label :party_id, class: "label" %>
<%= f.select :party_id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: "form-control custom-select", placeholder: "party_id" %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary", value: "Assign", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
</div>
================================================
FILE: app/views/wf/workitems/index.html.erb
================================================
<div class="card card-body">
<div>
<h2 class="">Stats</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">All</th>
<th scope="col">Enabled</th>
<th scope="col">Started</th>
<th scope="col">Canceled</th>
<th scope="col">Finished</th>
<th scope="col">Overridden</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).count, workitems_path %></td>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).enabled.count, workitems_path(state: :enabled) %></td>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).started.count, workitems_path(state: :started) %></td>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).canceled.count, workitems_path(state: :canceled) %></td>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).finished.count, workitems_path(state: :finished) %></td>
<td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).overridden.count, workitems_path(state: :overridden) %></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card card-body">
<div>
<h2>Workitems</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Transition</th>
<th scope="col">State</th>
<th scope="col">Holding User</th>
<th scope="col">Started At</th>
<th scope="col">Enabled At</th>
<th scope="col">Canceled At</th>
<th scope="col">Finished At</th>
<th scope="col">Overridden At</th>
<th scope="col">Deadline</th>
<th scope="col">Detail</th>
</tr>
</thead>
<tbody>
<% @workitems.each do |workitem| %>
<tr>
<td><%= link_to workitem.id, workitem_path(workitem) %></td>
<td>
<%= link_to workitem.transition.name, workflow_transition_path(workitem.workflow, workitem.transition) %> </td>
<td><%= workitem.state %></td>
<td><%= workitem.holding_user_id %></td>
<td><%= workitem.started_at %></td>
<td><%= workitem.enabled_at %></td>
<td><%= workitem.canceled_at %></td>
<td><%= workitem.finished_at %></td>
<td><%= workitem.overridden_at %></td>
<td><%= workitem.deadline %></td>
<td><%= link_to "Run", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<%= paginate @workitems, theme: 'twitter-bootstrap-4' %>
</div>
</div>
================================================
FILE: app/views/wf/workitems/pre_finish.html.erb
================================================
<%= form_with(model: @workitem, url: finish_workitem_path(@workitem), method: :put, local: true) do |f| %>
<% if @workitem.errors.any? %>
<article class="message is-danger">
<div class="message-header">
<p>
<%= pluralize(@workitem.errors.count, "error") %> prohibited this workitem from being saved:
</p>
</div>
<div class="message-body content">
<ul>
<% @workitem.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</article>
<% end %>
<% @workitem.transition.dynamic_assignments.each do |tt| %>
<%= f.fields_for :dynamic_assignments do |ff| %>
<div class="form-group">
<%= ff.label "Assign #{tt.name}", class: "label" %>
<%= ff.select tt.id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: "form-control custom-select" %>
</div>
<% end %>
<% end %>
<% if @workitem.transition.form %>
<%= f.fields_for :entry do |ff| %>
<% @workitem.transition.form.fields.each do |field| %>
<div class="form-group">
<%= ff.label field.name, class: "label" %>
<% if field.array? %>
<%= ff.select field.id, {}, {}, multiple: true, class: "form-control select2" %>
<% else %>
<%= ff.send(field.field_type_for_view, field.id, class: "form-control") %>
<% end %>
</div>
<% end %>
<% end %>
<% end %>
<div class="form-group">
<%= f.submit class: "btn btn-primary", value: "Done", data: {disable_with: 'Waiting...'} %>
</div>
<% end %>
<script>
$(function() {
$(".select2").select2({
tags: [],
theme: "bootstrap4",
tokenSeparators: [',', ' ']
});
});
</script>
================================================
FILE: app/views/wf/workitems/show.html.erb
================================================
<div class="card card-body">
<div class="float-right">
<%= link_to 'Back to Case', workflow_case_path(@workitem.workflow, @workitem.case), class: 'btn btn-primary' %>
</div>
<div>
<h2>Transition</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Trigger Limit</th>
<th scope="col">Trigger Type</th>
<th scope="col">Sort Order</th>
<th scope="col">Custom Form</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= @workitem.transition.id %></td>
<td><%= @workitem.transition.name %></td>
<td><%= @workitem.transition.description %></td>
<td><%= @workitem.transition.trigger_limit %></td>
<td><%= @workitem.transition.trigger_type %></td>
<td><%= @workitem.transition.sort_order %></td>
<td>
<% if @workitem.transition.form %>
<%= link_to @workitem.transition.form.name, form_path(@workitem.transition.form) %>
<% else %>
No Form
<% end %>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Detail</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Transition</th>
<th scope="col">State</th>
<th scope="col">Holding User</th>
<th scope="col">Started At</th>
<th scope="col">Enabled At</th>
<th scope="col">Canceled At</th>
<th scope="col">Finished At</th>
<th scope="col">Overridden At</th>
<th scope="col">Deadline</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= @workitem.id %></td>
<td>
<%= link_to @workitem.transition.name, workflow_transition_path(@workitem.workflow, @workitem.transition) %> </td>
<td><%= @workitem.state %></td>
<td><%= @workitem.holding_user_id %></td>
<td><%= @workitem.started_at %></td>
<td><%= @workitem.enabled_at %></td>
<td><%= @workitem.canceled_at %></td>
<td><%= @workitem.finished_at %></td>
<td><%= @workitem.overridden_at %></td>
<td><%= @workitem.deadline %></td>
</tr>
</tbody>
</table>
</div>
<% if @workitem.transition.form %>
<div>
<h2>Entries</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">User</th>
<th scope="col">Payload</th>
</tr>
</thead>
<tbody>
<% @workitem.entries.each do |entry| %>
<tr>
<td><%= entry.id %></td>
<td><%= entry.user_id %></td>
<td>
<pre>
<%= JSON.pretty_generate(entry.payload) %>
</pre>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
<div>
<h2>Assignments</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Party ID</th>
<th scope="col">Name</th>
<th scope="col">Created At</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<% @workitem.workitem_assignments.includes(:party).each do |assignment|%>
<tr>
<td><%= assignment.id %></td>
<td><%= assignment.party_id %></td>
<td>
<%= assignment.party.party_name %> </td>
<td><%= assignment.created_at %></td>
<td><%= link_to "Remove", workitem_workitem_assignment_path(@workitem, party_id: assignment.party_id), remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-info' %></td>
</tr>
<% end %>
<tr>
<td colspan="5">
<% if @workitem.transition.user? && (@workitem.started? || @workitem.enabled?) %>
<% if @workitem.finished_by?(wf_current_user) %>
<%= link_to "Pre Finish Workitem", pre_finish_workitem_path(@workitem), class: 'btn btn-sm btn-dark' %>
<% elsif @workitem.started_by?(wf_current_user)%>
<%= link_to "Start Workitem", start_workitem_path(@workitem), method: :put, class: 'btn btn-sm btn-dark' %>
<% else %>
You can not start workitem, Please assign to youself.
<% end %>
<%= link_to "Re Assign", new_workitem_workitem_assignment_path(@workitem), class: 'btn btn-success btn-sm' %>
<%= link_to "Assign Yourself", new_workitem_workitem_assignment_path(@workitem, party_id: wf_current_user.party&.id), class: 'btn btn-success btn-sm' %>
<%= link_to "Add Comment", new_workitem_comment_path(@workitem), class: 'btn btn-success btn-sm' %>
<% end %>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2>Comments</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Body</th>
<th scope="col">User</th>
<th scope="col">Created At</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<% @workitem.comments.order("id DESC").each do |comment| %>
<tr>
<td><%= comment.id %></td>
<td>
<%= comment.body %> </td>
<td><%= comment.user_id %></td>
<td><%= comment.created_at %></td>
<td><%= link_to "Delete", workitem_comment_path(@workitem, comment), remote: true, method: :delete, data: {confirm: 'confirm?', disable_with: 'Waiting...'}, class: 'btn btn-sm btn-info' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div>
<h2>All Workitems</h2>
<table class="table table-view">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Transition</th>
<th scope="col">State</th>
<th scope="col">Trigger Type</th>
<th scope="col">Holding User</th>
<th scope="col">Started At</th>
<th scope="col">Enabled At</th>
<th scope="col">Canceled At</th>
<th scope="col">Finished At</th>
<th scope="col">Overridden At</th>
<th scope="col">Deadline</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<% @workitem.case.workitems.includes(:transition).each do |workitem| %>
<tr>
<td><%= link_to workitem.id, workitem_path(workitem) %></td>
<td>
<%= workitem.transition.name %> </td>
<td><%= workitem.state %></td>
<td><%= workitem.transition.trigger_type %></td>
<td><%= workitem.holding_user_id %></td>
<td><%= workitem.started_at %></td>
<td><%= workitem.enabled_at %></td>
<td><%= workitem.canceled_at %></td>
<td><%= workitem.finished_at %></td>
<td><%= workitem.overridden_at %></td>
<td><%= workitem.deadline %></td>
<td><%= link_to "Run", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
================================================
FILE: bin/rails
================================================
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails gems
# installed from the root of your application.
ENGINE_ROOT = File.expand_path('..', __dir__)
ENGINE_PATH = File.expand_path('../lib/wf/engine', __dir__)
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
require 'rails/all'
require 'rails/engine/commands'
================================================
FILE: config/routes.rb
================================================
# frozen_string_literal: true
Wf::Engine.routes.draw do
resources :workflows do
resources :transitions
resources :places
resources :arcs
resources :cases
end
resources :arcs do
resources :guards
end
resources :forms do
resources :fields
end
resources :transitions do
resources :static_assignments
end
resources :workitems do
resources :workitem_assignments, only: %i[new create destroy]
resources :comments, only: %i[new create destroy]
member do
put :start
get :pre_finish
put :finish
end
end
root to: "workitems#index"
end
================================================
FILE: db/migrate/20200130201043_init.rb
================================================
# frozen_string_literal: true
class Init < ActiveRecord::Migration[6.0]
def change
create_table "wf_arcs", force: :cascade do |t|
t.bigint "workflow_id"
t.bigint "transition_id"
t.bigint "place_id"
t.integer "direction", default: 0, comment: "0-in, 1-out"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "guards_count", default: 0
end
create_table "wf_case_assignments", comment: "Manual per-case assignments of transition to parties", force: :cascade do |t|
t.bigint "case_id"
t.bigint "transition_id"
t.bigint "party_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index %w[case_id transition_id party_id], name: "wf_ctp_u", unique: true
end
create_table "wf_cases", force: :cascade do |t|
t.bigint "workflow_id"
t.string "targetable_type", comment: "point to target type of Application."
t.string "targetable_id", comment: "point to target ID of Application."
t.integer "state", default: 0, comment: "0-created, 1-active, 2-suspended, 3-canceled, 4-finished"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_comments", force: :cascade do |t|
t.bigint "workitem_id"
t.string "user_id"
t.text "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_wf_comments_on_user_id"
t.index ["workitem_id"], name: "index_wf_comments_on_workitem_id"
end
create_table "wf_demo_targets", comment: "For demo, useless.", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_field_values", force: :cascade do |t|
t.bigint "workflow_id"
t.bigint "transition_id"
t.bigint "form_id"
t.bigint "field_id"
t.text "value"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["field_id"], name: "index_wf_field_values_on_field_id"
t.index ["form_id"], name: "index_wf_field_values_on_form_id"
t.index ["transition_id"], name: "index_wf_field_values_on_transition_id"
t.index ["workflow_id"], name: "index_wf_field_values_on_workflow_id"
end
create_table "wf_fields", force: :cascade do |t|
t.string "name"
t.bigint "form_id"
t.integer "position", default: 0
t.integer "field_type", default: 0
t.string "field_type_name"
t.string "default_value"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["form_id"], name: "index_wf_fields_on_form_id"
end
create_table "wf_forms", force: :cascade do |t|
t.string "name"
t.text "description"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_groups", comment: "For demo", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_guards", force: :cascade do |t|
t.bigint "arc_id"
t.bigint "workflow_id"
t.string "fieldable_type"
t.string "fieldable_id"
t.string "op"
t.string "value"
t.string "exp"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["arc_id"], name: "index_wf_guards_on_arc_id"
t.index ["workflow_id"], name: "index_wf_guards_on_workflow_id"
end
create_table "wf_parties", comment: "for groups or roles or users or positions etc.", force: :cascade do |t|
t.string "partable_type"
t.string "partable_id"
t.string "party_name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index %w[partable_type partable_id], name: "index_wf_parties_on_partable_type_and_partable_id", unique: true
end
create_table "wf_places", force: :cascade do |t|
t.bigint "workflow_id"
t.string "name"
t.text "description"
t.integer "sort_order", default: 0
t.integer "place_type", default: 0, comment: "类型:0-start,1-normal,2-end"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_tokens", force: :cascade do |t|
t.bigint "workflow_id"
t.bigint "case_id"
t.string "targetable_type"
t.string "targetable_id"
t.bigint "place_id"
t.integer "state", default: 0, comment: "0-free, 1-locked, 2-canceled, 3-consumed"
t.bigint "locked_workitem_id"
if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)
t.datetime "produced_at", default: -> { "current_timestamp" }
elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)
t.datetime "produced_at", default: -> { "timezone('utc'::text, now())" }
end
t.datetime "locked_at"
t.datetime "canceled_at"
t.datetime "consumed_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_transition_static_assignments", comment: "pre assignment for transition", force: :cascade do |t|
t.bigint "party_id"
t.bigint "transition_id"
t.bigint "workflow_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index %w[transition_id party_id], name: "wf_tp_u", unique: true
end
create_table "wf_transitions", force: :cascade do |t|
t.string "name"
t.text "description"
t.bigint "workflow_id"
t.integer "sort_order", default: 0
t.integer "trigger_limit", comment: "use with timed trigger, after x minitues, trigger exec"
t.integer "trigger_type", default: 0, comment: "0-user,1-automatic, 2-message,3-time"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.bigint "form_id"
t.string "enable_callback", default: "Wf::Callbacks::EnableDefault"
t.string "fire_callback", default: "Wf::Callbacks::FireDefault"
t.string "notification_callback", default: "Wf::Callbacks::NotificationDefault"
t.string "time_callback", default: "Wf::Callbacks::TimeDefault"
t.string "deadline_callback", default: "Wf::Callbacks::DeadlineDefault"
t.string "hold_timeout_callback", default: "Wf::Callbacks::HoldTimeoutDefault"
t.string "assignment_callback", default: "Wf::Callbacks::AssignmentDefault"
t.string "unassignment_callback", default: "Wf::Callbacks::UnassignmentDefault"
end
create_table "wf_users", comment: "For demo", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.bigint "group_id"
end
create_table "wf_workflows", force: :cascade do |t|
t.string "name"
t.text "description"
t.boolean "is_valid", default: false
t.string "creator_id"
t.text "error_msg"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "wf_workitem_assignments", force: :cascade do |t|
t.bigint "party_id"
t.bigint "workitem_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index %w[workitem_id party_id], name: "wf_wp_u", unique: true
end
create_table "wf_workitems", force: :cascade do |t|
t.bigint "case_id"
t.bigint "workflow_id"
t.bigint "transition_id"
t.string "targetable_type", comment: "point to type of Application target: Task or Issue or PullRequest or Project etc."
t.string "targetable_id", comment: "point to id of Application target: task_id or issue_id or pull_request_id or project_id etc."
t.integer "state", default: 0, comment: "0-enabled, 1-started, 2-canceled, 3-finished,4-overridden"
if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)
t.datetime "enabled_at", default: -> { "current_timestamp" }
elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)
t.datetime "enabled_at", default: -> { "timezone('utc'::text, now())" }
end
t.datetime "started_at"
t.datetime "canceled_at"
t.datetime "finished_at"
t.datetime "overridden_at"
t.datetime "deadline", comment: ""
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.datetime "trigger_time", comment: "set when transition_trigger=TIME & trigger_limit present"
t.string "holding_user_id", comment: "id of App user"
if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)
t.json "payload", comment: "store user input payload for workitem."
elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)
t.json "payload", default: {}, comment: "store user input payload for workitem."
end
t.index %w[state trigger_time], name: "index_wf_workitems_on_state_and_trigger_time"
end
end
end
================================================
FILE: db/migrate/20200130201641_init_some_data.rb
================================================
# frozen_string_literal: true
class InitSomeData < ActiveRecord::Migration[6.0]
def change
5.times do |i|
Wf::Group.create(name: "Group#{i}")
end
10.times do |i|
Wf::User.create(name: "User#{i}", group: Wf::Group.all.sample)
end
10.times { |i| Wf::DemoTarget.create(name: "target #{i}") }
end
end
================================================
FILE: db/migrate/20200131200455_create_wf_entries.rb
================================================
# frozen_string_literal: true
class CreateWfEntries < ActiveRecord::Migration[6.0]
def change
create_table :wf_entries, comment: "user input data for workitem with form." do |t|
t.string :user_id, index: true
t.bigint :workitem_id, index: true
if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)
t.json "payload"
elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)
t.json "payload", default: {}
end
t.timestamps
end
remove_column :wf_workitems, :payload
add_column :wf_field_values, :entry_id, :bigint, index: true
add_index :wf_entries, %i[workitem_id user_id], unique: true
end
end
================================================
FILE: db/migrate/20200201001543_add_target_field_name_for_guard.rb
================================================
# frozen_string_literal: true
class AddTargetFieldNameForGuard < ActiveRecord::Migration[6.0]
def change
add_column :wf_guards, :target_attr_name, :string, comment: "point to workflow targetable's attribute"
end
end
================================================
FILE: db/migrate/20200212120019_remove_targetable_from_workitem.rb
================================================
# frozen_string_literal: true
class RemoveTargetableFromWorkitem < ActiveRecord::Migration[6.0]
def change
remove_column :wf_workitems, :targetable_id
remove_column :wf_workitems, :targetable_type
end
end
================================================
FILE: db/migrate/20200213085258_add_formable.rb
================================================
# frozen_string_literal: true
class AddFormable < ActiveRecord::Migration[6.0]
def change
add_column :wf_transitions, :form_type, :string, default: "Wf::Form"
add_index :wf_transitions, %i[form_type form_id]
end
end
================================================
FILE: db/migrate/20200213125753_add_form_id_for_entry.rb
================================================
# frozen_string_literal: true
class AddFormIdForEntry < ActiveRecord::Migration[6.0]
def change
add_column :wf_entries, :form_id, :bigint, index: true
end
end
================================================
FILE: db/migrate/20200213130900_remove_workflow_id_from_form_related.rb
================================================
# frozen_string_literal: true
class RemoveWorkflowIdFromFormRelated < ActiveRecord::Migration[6.0]
def change
remove_column :wf_field_values, :transition_id
remove_column :wf_field_values, :workflow_id
end
end
================================================
FILE: db/migrate/20200220070839_remove_unused_column.rb
================================================
# frozen_string_literal: true
class RemoveUnusedColumn < ActiveRecord::Migration[6.0]
def change
remove_column :wf_guards, :target_attr_name
end
end
================================================
FILE: db/migrate/20200220072512_add_sub_workflow.rb
================================================
# frozen_string_literal: true
class AddSubWorkflow < ActiveRecord::Migration[6.0]
def change
add_column :wf_transitions, :sub_workflow_id, :bigint, index: true
add_column :wf_cases, :started_by_workitem_id, :bigint, index: true, comment: "As a sub workflow instance, it is started by one workitem."
end
end
================================================
FILE: db/migrate/20200222150432_add_multi_instance.rb
================================================
# frozen_string_literal: true
class AddMultiInstance < ActiveRecord::Migration[6.0]
def change
add_column :wf_transitions, :multiple_instance, :boolean, default: false, comment: "multiple instance mode or not"
add_column :wf_transitions, :finish_condition, :string, comment: "set finish condition for parent workitem.", default: "Wf::MultipleInstances::AllFinish"
add_column :wf_workitems, :children_count, :integer, default: 0
add_column :wf_workitems, :children_finished_count, :integer, default: 0
add_column :wf_workitems, :forked, :boolean, default: false
add_column :wf_workitems, :parent_id, :bigint, comment: "parent workitem id"
end
end
================================================
FILE: db/migrate/20200226195134_add_dynamic_assign_by.rb
================================================
# frozen_string_literal: true
class AddDynamicAssignBy < ActiveRecord::Migration[6.0]
def change
add_column :wf_transitions, :dynamic_assign_by_id, :bigint, comment: "dynamic assign by other transition", index: true
end
end
================================================
FILE: lib/tasks/wf_tasks.rake
================================================
# frozen_string_literal: true
desc "Wf tasks"
task wf: :environment do
url = "http://service-technology.org/files/lola/lola-2.0.tar.gz"
path = Rails.root.join("tmp", "lola.tar.gz").to_s
puts "Downloading, wait!"
puts `wget http://service-technology.org/files/lola/lola-2.0.tar.gz -v -t0 -O #{path}` unless File.exist?(path)
puts `cd #{Rails.root.join("tmp")} && tar -zxvf lola.tar.gz`
puts `cd #{Rails.root.join("tmp/lola-2.0")} && ./configure`
puts `cd #{Rails.root.join("tmp/lola-2.0")} && make`
puts `cd #{Rails.root.join("tmp/lola-2.0")} && sudo make install`
puts `lola --help`
end
================================================
FILE: lib/wf/engine.rb
================================================
# frozen_string_literal: true
module Wf
class Engine < ::Rails::Engine
isolate_namespace Wf
config.autoload_paths += %W[
#{config.root}/app/models/wf/concerns
]
config.to_prepare do
require_dependency(Rails.root + "config/initializers/wf_config.rb")
rescue LoadError
puts("config/initializers/wf_config.rb not found.")
end
end
end
require "bootstrap"
require "bootstrap4-kaminari-views"
require "jquery-rails"
require "kaminari"
require "simple_command"
require "loaf"
require "graphviz"
require "rgl/adjacency"
require "rgl/dijkstra"
require "rgl/topsort"
require "rgl/traversal"
require "rgl/path"
require "active_record/connection_adapters/postgresql_adapter.rb"
require "select2-rails"
require "mini_racer"
================================================
FILE: lib/wf/version.rb
================================================
# frozen_string_literal: true
module Wf
VERSION = "0.2.5"
end
================================================
FILE: lib/wf.rb
================================================
# frozen_string_literal: true
require "wf/engine"
module Wf
class << self
attr_accessor :enable_callbacks
attr_accessor :fire_callbacks
attr_accessor :assignment_callbacks
attr_accessor :unassignment_callbacks
attr_accessor :notification_callbacks
attr_accessor :time_callbacks
attr_accessor :deadline_callbacks
attr_accessor :hold_timeout_callbacks
attr_accessor :form_class
attr_accessor :entry_class
attr_accessor :field_class
attr_accessor :user_class
attr_accessor :org_classes
attr_accessor :finish_conditions
attr_accessor :use_lola
end
self.enable_callbacks = ["Wf::Callbacks::EnableDefault"]
self.fire_callbacks = ["Wf::Callbacks::FireDefault"]
self.assignment_callbacks = ["Wf::Callbacks::AssignmentDefault"]
self.unassignment_callbacks = ["Wf::Callbacks::UnassignmentDefault"]
self.notification_callbacks = ["Wf::Callbacks::NotificationDefault"]
self.deadline_callbacks = ["Wf::Callbacks::DeadlineDefault"]
self.time_callbacks = ["Wf::Callbacks::TimeDefault"]
self.hold_timeout_callbacks = ["Wf::Callbacks::HoldTimeoutDefault"]
self.form_class = "::Wf::Form"
self.entry_class = "::Wf::Entry"
self.field_class = "::Wf::Field"
self.user_class = "::Wf::User"
self.org_classes = { group: "::Wf::Group" }
self.finish_conditions = ["Wf::MultipleInstances::AllFinish"]
self.use_lola = false
end
================================================
FILE: lola.md
================================================
## LoLA
LoLA is a Petri nets model-checking tool. To install LoLA, download lola-2.0.tar.gz from http://home.gna.org
gitextract_22cx_jl_/ ├── .editorconfig ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── gempush.yml ├── .gitignore ├── .rubocop.yml ├── FormSpec.md ├── Gemfile ├── Guard.md ├── LICENSE ├── MIT-LICENSE ├── README.md ├── Rakefile ├── acts_as_party.md ├── app/ │ ├── assets/ │ │ ├── config/ │ │ │ └── wf_manifest.js │ │ ├── images/ │ │ │ └── wf/ │ │ │ └── .keep │ │ ├── javascripts/ │ │ │ └── wf/ │ │ │ └── application.js │ │ └── stylesheets/ │ │ └── wf/ │ │ ├── application.scss │ │ ├── arcs.css │ │ ├── cases.css │ │ ├── comments.css │ │ ├── fields.css │ │ ├── forms.css │ │ ├── guards.css │ │ ├── places.css │ │ ├── static_assignments.css │ │ ├── transitions.css │ │ ├── uikit/ │ │ │ ├── _colors.scss │ │ │ ├── _variables.scss │ │ │ ├── alert.scss │ │ │ ├── button.scss │ │ │ ├── card.scss │ │ │ ├── index.scss │ │ │ ├── navbar.scss │ │ │ └── table.scss │ │ ├── workflows.css │ │ ├── workitem_assignments.css │ │ └── workitems.css │ ├── controllers/ │ │ └── wf/ │ │ ├── application_controller.rb │ │ ├── arcs_controller.rb │ │ ├── cases_controller.rb │ │ ├── comments_controller.rb │ │ ├── fields_controller.rb │ │ ├── forms_controller.rb │ │ ├── guards_controller.rb │ │ ├── places_controller.rb │ │ ├── static_assignments_controller.rb │ │ ├── transitions_controller.rb │ │ ├── workflows_controller.rb │ │ ├── workitem_assignments_controller.rb │ │ └── workitems_controller.rb │ ├── helpers/ │ │ └── wf/ │ │ ├── application_helper.rb │ │ ├── arcs_helper.rb │ │ ├── cases_helper.rb │ │ ├── comments_helper.rb │ │ ├── fields_helper.rb │ │ ├── forms_helper.rb │ │ ├── guards_helper.rb │ │ ├── places_helper.rb │ │ ├── static_assignments_helper.rb │ │ ├── transitions_helper.rb │ │ ├── workflows_helper.rb │ │ ├── workitem_assignments_helper.rb │ │ └── workitems_helper.rb │ ├── jobs/ │ │ └── wf/ │ │ ├── application_job.rb │ │ └── fire_timed_workitem_job.rb │ ├── mailers/ │ │ └── wf/ │ │ └── application_mailer.rb │ ├── models/ │ │ └── wf/ │ │ ├── acts_as_party.rb │ │ ├── application_record.rb │ │ ├── arc.rb │ │ ├── callbacks/ │ │ │ ├── assignment_default.rb │ │ │ ├── deadline_default.rb │ │ │ ├── enable_default.rb │ │ │ ├── fire_default.rb │ │ │ ├── hold_timeout_default.rb │ │ │ ├── notification_default.rb │ │ │ ├── time_default.rb │ │ │ └── unassignment_default.rb │ │ ├── case.rb │ │ ├── case_assignment.rb │ │ ├── case_command/ │ │ │ ├── add_comment.rb │ │ │ ├── add_manual_assignment.rb │ │ │ ├── add_token.rb │ │ │ ├── add_workitem_assignment.rb │ │ │ ├── begin_workitem_action.rb │ │ │ ├── cancel.rb │ │ │ ├── cancel_workitem.rb │ │ │ ├── clear_manual_assignments.rb │ │ │ ├── clear_workitem_assignments.rb │ │ │ ├── consume_token.rb │ │ │ ├── create_entry.rb │ │ │ ├── enable_transitions.rb │ │ │ ├── end_workitem_action.rb │ │ │ ├── finish_workitem.rb │ │ │ ├── finished_p.rb │ │ │ ├── fire_message_transition.rb │ │ │ ├── fire_transition_internal.rb │ │ │ ├── lock_token.rb │ │ │ ├── new.rb │ │ │ ├── release_token.rb │ │ │ ├── remove_manual_assignment.rb │ │ │ ├── remove_workitem_assignment.rb │ │ │ ├── resume.rb │ │ │ ├── set_workitem_assignments.rb │ │ │ ├── start_case.rb │ │ │ ├── start_workitem.rb │ │ │ ├── suspend.rb │ │ │ ├── sweep_automatic_transitions.rb │ │ │ ├── sweep_timed_transitions.rb │ │ │ └── workitem_action.rb │ │ ├── comment.rb │ │ ├── demo_target.rb │ │ ├── entry.rb │ │ ├── field.rb │ │ ├── field_value.rb │ │ ├── form.rb │ │ ├── group.rb │ │ ├── guard.rb │ │ ├── lola.rb │ │ ├── multiple_instances/ │ │ │ └── all_finish.rb │ │ ├── party.rb │ │ ├── place.rb │ │ ├── token.rb │ │ ├── transition.rb │ │ ├── transition_static_assignment.rb │ │ ├── user.rb │ │ ├── workflow.rb │ │ ├── workitem.rb │ │ └── workitem_assignment.rb │ └── views/ │ ├── layouts/ │ │ └── wf/ │ │ ├── _alert.html.erb │ │ ├── _footer.html.erb │ │ ├── _nav.html.erb │ │ ├── _notice.html.erb │ │ └── application.html.erb │ └── wf/ │ ├── arcs/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── cases/ │ │ ├── _form.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── comments/ │ │ └── new.html.erb │ ├── fields/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── forms/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── guards/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── places/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── static_assignments/ │ │ ├── _form.html.erb │ │ └── new.html.erb │ ├── transitions/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── workflows/ │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── workitem_assignments/ │ │ └── new.html.erb │ └── workitems/ │ ├── index.html.erb │ ├── pre_finish.html.erb │ └── show.html.erb ├── bin/ │ └── rails ├── config/ │ └── routes.rb ├── db/ │ └── migrate/ │ ├── 20200130201043_init.rb │ ├── 20200130201641_init_some_data.rb │ ├── 20200131200455_create_wf_entries.rb │ ├── 20200201001543_add_target_field_name_for_guard.rb │ ├── 20200212120019_remove_targetable_from_workitem.rb │ ├── 20200213085258_add_formable.rb │ ├── 20200213125753_add_form_id_for_entry.rb │ ├── 20200213130900_remove_workflow_id_from_form_related.rb │ ├── 20200220070839_remove_unused_column.rb │ ├── 20200220072512_add_sub_workflow.rb │ ├── 20200222150432_add_multi_instance.rb │ └── 20200226195134_add_dynamic_assign_by.rb ├── lib/ │ ├── tasks/ │ │ └── wf_tasks.rake │ ├── wf/ │ │ ├── engine.rb │ │ └── version.rb │ └── wf.rb ├── lola.md ├── screenshots/ │ └── .keep ├── test/ │ ├── controllers/ │ │ └── wf/ │ │ ├── arcs_controller_test.rb │ │ ├── cases_controller_test.rb │ │ ├── comments_controller_test.rb │ │ ├── fields_controller_test.rb │ │ ├── forms_controller_test.rb │ │ ├── guards_controller_test.rb │ │ ├── places_controller_test.rb │ │ ├── static_assignments_controller_test.rb │ │ ├── transitions_controller_test.rb │ │ ├── workflows_controller_test.rb │ │ ├── workitem_assignments_controller_test.rb │ │ └── workitems_controller_test.rb │ ├── dummy/ │ │ ├── .ruby-version │ │ ├── Rakefile │ │ ├── app/ │ │ │ ├── assets/ │ │ │ │ ├── config/ │ │ │ │ │ └── manifest.js │ │ │ │ ├── images/ │ │ │ │ │ └── .keep │ │ │ │ └── stylesheets/ │ │ │ │ └── application.css │ │ │ ├── channels/ │ │ │ │ └── application_cable/ │ │ │ │ ├── channel.rb │ │ │ │ └── connection.rb │ │ │ ├── controllers/ │ │ │ │ ├── application_controller.rb │ │ │ │ └── concerns/ │ │ │ │ └── .keep │ │ │ ├── helpers/ │ │ │ │ └── application_helper.rb │ │ │ ├── javascript/ │ │ │ │ └── packs/ │ │ │ │ └── application.js │ │ │ ├── jobs/ │ │ │ │ └── application_job.rb │ │ │ ├── mailers/ │ │ │ │ └── application_mailer.rb │ │ │ ├── models/ │ │ │ │ ├── application_record.rb │ │ │ │ ├── concerns/ │ │ │ │ │ └── .keep │ │ │ │ ├── entry.rb │ │ │ │ ├── field.rb │ │ │ │ ├── field_value.rb │ │ │ │ └── form.rb │ │ │ └── views/ │ │ │ └── layouts/ │ │ │ ├── application.html.erb │ │ │ ├── mailer.html.erb │ │ │ └── mailer.text.erb │ │ ├── bin/ │ │ │ ├── rails │ │ │ ├── rake │ │ │ └── setup │ │ ├── config/ │ │ │ ├── application.rb │ │ │ ├── boot.rb │ │ │ ├── cable.yml │ │ │ ├── database.yml │ │ │ ├── environment.rb │ │ │ ├── environments/ │ │ │ │ ├── development.rb │ │ │ │ ├── production.rb │ │ │ │ └── test.rb │ │ │ ├── initializers/ │ │ │ │ ├── application_controller_renderer.rb │ │ │ │ ├── assets.rb │ │ │ │ ├── backtrace_silencers.rb │ │ │ │ ├── content_security_policy.rb │ │ │ │ ├── cookies_serializer.rb │ │ │ │ ├── filter_parameter_logging.rb │ │ │ │ ├── inflections.rb │ │ │ │ ├── mime_types.rb │ │ │ │ ├── my_assignment_callback.rb │ │ │ │ ├── wf_config.rb │ │ │ │ └── wrap_parameters.rb │ │ │ ├── locales/ │ │ │ │ └── en.yml │ │ │ ├── mysql_database.yml │ │ │ ├── puma.rb │ │ │ ├── routes.rb │ │ │ ├── spring.rb │ │ │ └── storage.yml │ │ ├── config.ru │ │ ├── db/ │ │ │ ├── migrate/ │ │ │ │ ├── 20200213081814_new_form.rb │ │ │ │ ├── 20200213133942_add_form_id_in_entry1.rb │ │ │ │ └── 20200214005535_add_entry_id_for_field_values1.rb │ │ │ ├── schema.rb │ │ │ └── seeds.rb │ │ ├── lib/ │ │ │ └── assets/ │ │ │ └── .keep │ │ ├── log/ │ │ │ └── .keep │ │ ├── public/ │ │ │ ├── 404.html │ │ │ ├── 422.html │ │ │ └── 500.html │ │ └── storage/ │ │ └── .keep │ ├── fixtures/ │ │ └── wf/ │ │ ├── case_assignments.yml │ │ ├── comments.yml │ │ ├── demo_targets.yml │ │ ├── entries.yml │ │ ├── field_values.yml │ │ ├── fields.yml │ │ ├── forms.yml │ │ ├── guards.yml │ │ ├── parties.yml │ │ ├── transition_static_assignments.yml │ │ ├── users.yml │ │ └── workitem_assignments.yml │ ├── integration/ │ │ └── navigation_test.rb │ ├── models/ │ │ └── wf/ │ │ ├── case_assignment_test.rb │ │ ├── comment_test.rb │ │ ├── demo_target_test.rb │ │ ├── entry_test.rb │ │ ├── field_test.rb │ │ ├── field_value_test.rb │ │ ├── form_test.rb │ │ ├── guard_test.rb │ │ ├── party_test.rb │ │ ├── transition_static_assignment_test.rb │ │ ├── user_test.rb │ │ ├── wf_test.rb │ │ └── workitem_assignment_test.rb │ └── test_helper.rb └── wf.gemspec
SYMBOL INDEX (493 symbols across 148 files)
FILE: app/controllers/wf/application_controller.rb
type Wf (line 3) | module Wf
class ApplicationController (line 4) | class ApplicationController < ::ApplicationController
method wf_current_user (line 10) | def wf_current_user
FILE: app/controllers/wf/arcs_controller.rb
type Wf (line 5) | module Wf
class ArcsController (line 6) | class ArcsController < ApplicationController
method new (line 9) | def new
method create (line 15) | def create
method destroy (line 25) | def destroy
method show (line 32) | def show
method edit (line 38) | def edit
method update (line 45) | def update
method arc_params (line 57) | def arc_params
FILE: app/controllers/wf/cases_controller.rb
type Wf (line 5) | module Wf
class CasesController (line 6) | class CasesController < ApplicationController
method new (line 8) | def new
method create (line 14) | def create
method index (line 21) | def index
method show (line 29) | def show
method destroy (line 35) | def destroy
method case_params (line 44) | def case_params
FILE: app/controllers/wf/comments_controller.rb
type Wf (line 5) | module Wf
class CommentsController (line 6) | class CommentsController < ApplicationController
method new (line 9) | def new
method create (line 17) | def create
method destroy (line 23) | def destroy
FILE: app/controllers/wf/fields_controller.rb
type Wf (line 5) | module Wf
class FieldsController (line 6) | class FieldsController < ApplicationController
method new (line 9) | def new
method create (line 15) | def create
method destroy (line 25) | def destroy
method edit (line 32) | def edit
method update (line 38) | def update
method field_params (line 50) | def field_params
FILE: app/controllers/wf/forms_controller.rb
type Wf (line 5) | module Wf
class FormsController (line 6) | class FormsController < ApplicationController
method index (line 9) | def index
method new (line 13) | def new
method edit (line 17) | def edit
method show (line 21) | def show
method destroy (line 25) | def destroy
method update (line 34) | def update
method create (line 43) | def create
method form_params (line 55) | def form_params
FILE: app/controllers/wf/guards_controller.rb
type Wf (line 5) | module Wf
class GuardsController (line 6) | class GuardsController < ApplicationController
method new (line 8) | def new
method create (line 15) | def create
method destroy (line 27) | def destroy
method edit (line 34) | def edit
method update (line 41) | def update
method guard_params (line 54) | def guard_params
FILE: app/controllers/wf/places_controller.rb
type Wf (line 5) | module Wf
class PlacesController (line 6) | class PlacesController < ApplicationController
method new (line 8) | def new
method create (line 14) | def create
method destroy (line 24) | def destroy
method edit (line 31) | def edit
method update (line 37) | def update
method place_params (line 49) | def place_params
FILE: app/controllers/wf/static_assignments_controller.rb
type Wf (line 5) | module Wf
class StaticAssignmentsController (line 6) | class StaticAssignmentsController < ApplicationController
method new (line 7) | def new
method create (line 12) | def create
method destroy (line 23) | def destroy
method permit_params (line 30) | def permit_params
FILE: app/controllers/wf/transitions_controller.rb
type Wf (line 5) | module Wf
class TransitionsController (line 6) | class TransitionsController < ApplicationController
method new (line 9) | def new
method show (line 15) | def show
method create (line 20) | def create
method edit (line 31) | def edit
method destroy (line 37) | def destroy
method update (line 44) | def update
method transition_params (line 57) | def transition_params
FILE: app/controllers/wf/workflows_controller.rb
type Wf (line 5) | module Wf
class WorkflowsController (line 6) | class WorkflowsController < ApplicationController
method index (line 8) | def index
method new (line 12) | def new
method edit (line 16) | def edit
method show (line 21) | def show
method destroy (line 25) | def destroy
method update (line 34) | def update
method create (line 43) | def create
method workflow_params (line 55) | def workflow_params
FILE: app/controllers/wf/workitem_assignments_controller.rb
type Wf (line 5) | module Wf
class WorkitemAssignmentsController (line 6) | class WorkitemAssignmentsController < ApplicationController
method new (line 8) | def new
method create (line 16) | def create
method destroy (line 23) | def destroy
FILE: app/controllers/wf/workitems_controller.rb
type Wf (line 5) | module Wf
class WorkitemsController (line 6) | class WorkitemsController < ApplicationController
method index (line 13) | def index
method show (line 20) | def show
method start (line 25) | def start
method pre_finish (line 33) | def pre_finish
method finish (line 39) | def finish
method finish_and_redirect (line 60) | def finish_and_redirect
method find_workitem (line 72) | def find_workitem
method check_start (line 76) | def check_start
method check_finish (line 82) | def check_finish
FILE: app/helpers/wf/application_helper.rb
type Wf (line 3) | module Wf
type ApplicationHelper (line 4) | module ApplicationHelper
FILE: app/helpers/wf/arcs_helper.rb
type Wf (line 3) | module Wf
type ArcsHelper (line 4) | module ArcsHelper
FILE: app/helpers/wf/cases_helper.rb
type Wf (line 3) | module Wf
type CasesHelper (line 4) | module CasesHelper
FILE: app/helpers/wf/comments_helper.rb
type Wf (line 3) | module Wf
type CommentsHelper (line 4) | module CommentsHelper
FILE: app/helpers/wf/fields_helper.rb
type Wf (line 3) | module Wf
type FieldsHelper (line 4) | module FieldsHelper
FILE: app/helpers/wf/forms_helper.rb
type Wf (line 3) | module Wf
type FormsHelper (line 4) | module FormsHelper
FILE: app/helpers/wf/guards_helper.rb
type Wf (line 3) | module Wf
type GuardsHelper (line 4) | module GuardsHelper
FILE: app/helpers/wf/places_helper.rb
type Wf (line 3) | module Wf
type PlacesHelper (line 4) | module PlacesHelper
FILE: app/helpers/wf/static_assignments_helper.rb
type Wf (line 3) | module Wf
type StaticAssignmentsHelper (line 4) | module StaticAssignmentsHelper
FILE: app/helpers/wf/transitions_helper.rb
type Wf (line 3) | module Wf
type TransitionsHelper (line 4) | module TransitionsHelper
FILE: app/helpers/wf/workflows_helper.rb
type Wf (line 3) | module Wf
type WorkflowsHelper (line 4) | module WorkflowsHelper
FILE: app/helpers/wf/workitem_assignments_helper.rb
type Wf (line 3) | module Wf
type WorkitemAssignmentsHelper (line 4) | module WorkitemAssignmentsHelper
FILE: app/helpers/wf/workitems_helper.rb
type Wf (line 3) | module Wf
type WorkitemsHelper (line 4) | module WorkitemsHelper
FILE: app/jobs/wf/application_job.rb
type Wf (line 3) | module Wf
class ApplicationJob (line 4) | class ApplicationJob < ActiveJob::Base
FILE: app/jobs/wf/fire_timed_workitem_job.rb
type Wf (line 3) | module Wf
class FireTimedWorkitemJob (line 4) | class FireTimedWorkitemJob < ApplicationJob
method perform (line 7) | def perform(workitem_id)
FILE: app/mailers/wf/application_mailer.rb
type Wf (line 3) | module Wf
class ApplicationMailer (line 4) | class ApplicationMailer < ActionMailer::Base
FILE: app/models/wf/acts_as_party.rb
type Wf (line 5) | module Wf
type ActsAsParty (line 6) | module ActsAsParty
type ClassMethods (line 13) | module ClassMethods
function acts_as_party (line 14) | def acts_as_party(options = { user: false, party_name: :name })
FILE: app/models/wf/application_record.rb
type Wf (line 3) | module Wf
class ApplicationRecord (line 4) | class ApplicationRecord < ActiveRecord::Base
FILE: app/models/wf/arc.rb
type Wf (line 17) | module Wf
class Arc (line 18) | class Arc < ApplicationRecord
method name (line 34) | def name
FILE: app/models/wf/callbacks/assignment_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class AssignmentDefault (line 4) | class AssignmentDefault < ApplicationJob
method perform (line 7) | def perform(_workitem_id)
FILE: app/models/wf/callbacks/deadline_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class DeadlineDefault (line 4) | class DeadlineDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/enable_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class EnableDefault (line 4) | class EnableDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/fire_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class FireDefault (line 4) | class FireDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/hold_timeout_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class HoldTimeoutDefault (line 4) | class HoldTimeoutDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/notification_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class NotificationDefault (line 4) | class NotificationDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/time_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class TimeDefault (line 4) | class TimeDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/callbacks/unassignment_default.rb
type Wf::Callbacks (line 3) | module Wf::Callbacks
class UnassignmentDefault (line 4) | class UnassignmentDefault < ApplicationJob
method perform (line 7) | def perform(*guests)
FILE: app/models/wf/case.rb
type Wf (line 17) | module Wf
class Case (line 18) | class Case < ApplicationRecord
method can_fire? (line 35) | def can_fire?(transition)
method name (line 42) | def name
FILE: app/models/wf/case_assignment.rb
type Wf (line 16) | module Wf
class CaseAssignment (line 17) | class CaseAssignment < ApplicationRecord
FILE: app/models/wf/case_command/add_comment.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class AddComment (line 4) | class AddComment
method initialize (line 7) | def initialize(workitem, comment, user)
method call (line 13) | def call
FILE: app/models/wf/case_command/add_manual_assignment.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class AddManualAssignment (line 4) | class AddManualAssignment
method initialize (line 7) | def initialize(wf_case, transition, party)
method call (line 13) | def call
FILE: app/models/wf/case_command/add_token.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class AddToken (line 4) | class AddToken
method initialize (line 7) | def initialize(wf_case, place)
method call (line 12) | def call
FILE: app/models/wf/case_command/add_workitem_assignment.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class AddWorkitemAssignment (line 4) | class AddWorkitemAssignment
method initialize (line 7) | def initialize(workitem, party, permanent = true)
method call (line 13) | def call
FILE: app/models/wf/case_command/begin_workitem_action.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class BeginWorkitemAction (line 4) | class BeginWorkitemAction
method initialize (line 7) | def initialize(workitem, user, action = :start)
method call (line 13) | def call
FILE: app/models/wf/case_command/cancel.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class Cancel (line 4) | class Cancel
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/cancel_workitem.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class CancelWorkitem (line 4) | class CancelWorkitem
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/clear_manual_assignments.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class ClearManualAssignments (line 4) | class ClearManualAssignments
method initialize (line 7) | def initialize(wf_case, transition)
method call (line 12) | def call
FILE: app/models/wf/case_command/clear_workitem_assignments.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class ClearWorkitemAssignments (line 4) | class ClearWorkitemAssignments
method initialize (line 7) | def initialize(workitem, permanent = true)
method call (line 12) | def call
FILE: app/models/wf/case_command/consume_token.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class ConsumeToken (line 4) | class ConsumeToken
method initialize (line 7) | def initialize(wf_case, place, locked_item = nil)
method call (line 13) | def call
FILE: app/models/wf/case_command/create_entry.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class CreateEntry (line 4) | class CreateEntry
method initialize (line 7) | def initialize(form, workitem, user, params)
method call (line 14) | def call
method create_entry (line 23) | def create_entry
FILE: app/models/wf/case_command/enable_transitions.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class EnableTransitions (line 4) | class EnableTransitions
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/end_workitem_action.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class EndWorkitemAction (line 4) | class EndWorkitemAction
method initialize (line 7) | def initialize(workitem, user, action = :start)
method call (line 13) | def call
FILE: app/models/wf/case_command/finish_workitem.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class FinishWorkitem (line 4) | class FinishWorkitem
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/finished_p.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class FinishedP (line 4) | class FinishedP
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/fire_message_transition.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class FireMessageTransition (line 4) | class FireMessageTransition
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/fire_transition_internal.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class FireTransitionInternal (line 4) | class FireTransitionInternal
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/lock_token.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class LockToken (line 4) | class LockToken
method initialize (line 7) | def initialize(wf_case, place, workitem)
method call (line 13) | def call
FILE: app/models/wf/case_command/new.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class New (line 4) | class New
method initialize (line 7) | def initialize(workflow, target = nil, started_by = nil)
method call (line 13) | def call
FILE: app/models/wf/case_command/release_token.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class ReleaseToken (line 4) | class ReleaseToken
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/remove_manual_assignment.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class RemoveManualAssignment (line 4) | class RemoveManualAssignment
method initialize (line 7) | def initialize(wf_case, transition, party)
method call (line 13) | def call
FILE: app/models/wf/case_command/remove_workitem_assignment.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class RemoveWorkitemAssignment (line 4) | class RemoveWorkitemAssignment
method initialize (line 7) | def initialize(workitem, party, permanent = true)
method call (line 13) | def call
FILE: app/models/wf/case_command/resume.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class Resume (line 4) | class Resume
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/set_workitem_assignments.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class SetWorkitemAssignments (line 4) | class SetWorkitemAssignments
method initialize (line 7) | def initialize(workitem)
method call (line 11) | def call
FILE: app/models/wf/case_command/start_case.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class StartCase (line 4) | class StartCase
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/start_workitem.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class StartWorkitem (line 4) | class StartWorkitem
method initialize (line 7) | def initialize(workitem, user)
method call (line 12) | def call
FILE: app/models/wf/case_command/suspend.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class Suspend (line 4) | class Suspend
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/sweep_automatic_transitions.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class SweepAutomaticTransitions (line 4) | class SweepAutomaticTransitions
method initialize (line 7) | def initialize(wf_case)
method call (line 11) | def call
FILE: app/models/wf/case_command/sweep_timed_transitions.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class SweepTimedTransitions (line 4) | class SweepTimedTransitions
method call (line 7) | def call
FILE: app/models/wf/case_command/workitem_action.rb
type Wf::CaseCommand (line 3) | module Wf::CaseCommand
class WorkitemAction (line 4) | class WorkitemAction
method initialize (line 7) | def initialize(workitem, user, action = :start)
method call (line 13) | def call
FILE: app/models/wf/comment.rb
type Wf (line 16) | module Wf
class Comment (line 17) | class Comment < ApplicationRecord
FILE: app/models/wf/demo_target.rb
type Wf (line 14) | module Wf
class DemoTarget (line 15) | class DemoTarget < ApplicationRecord
FILE: app/models/wf/entry.rb
type Wf (line 16) | module Wf
class Entry (line 17) | class Entry < ApplicationRecord
method json (line 27) | def json
method for_mini_racer (line 31) | def for_mini_racer
method update_payload! (line 35) | def update_payload!
FILE: app/models/wf/field.rb
type Wf (line 18) | module Wf
class Field (line 19) | class Field < ApplicationRecord
method field_type_for_view (line 44) | def field_type_for_view
method array? (line 63) | def array?
method type_for_cast (line 67) | def type_for_cast
FILE: app/models/wf/field_value.rb
type Wf (line 16) | module Wf
class FieldValue (line 17) | class FieldValue < ApplicationRecord
method value_after_cast (line 22) | def value_after_cast
method value= (line 36) | def value=(v)
method value (line 44) | def value
FILE: app/models/wf/form.rb
type Wf (line 14) | module Wf
class Form (line 15) | class Form < ApplicationRecord
FILE: app/models/wf/group.rb
type Wf (line 13) | module Wf
class Group (line 14) | class Group < ApplicationRecord
FILE: app/models/wf/guard.rb
type Wf (line 19) | module Wf
class Guard (line 20) | class Guard < ApplicationRecord
method value_after_cast (line 40) | def value_after_cast
method pass? (line 45) | def pass?(entry, workitem)
method check_exp (line 53) | def check_exp(_entry, workitem)
method check_fieldable (line 62) | def check_fieldable(entry)
method yes_or_no? (line 69) | def yes_or_no?(input_value, setting_value)
method inspect (line 87) | def inspect
method validate_exp_and_fieldable (line 95) | def validate_exp_and_fieldable
FILE: app/models/wf/lola.rb
type Wf (line 3) | module Wf
class Lola (line 4) | class Lola
method initialize (line 6) | def initialize(workflow)
method to_text (line 13) | def to_text
method json_path (line 39) | def json_path(bucket)
method lola_path (line 43) | def lola_path
method generate_lola_file! (line 47) | def generate_lola_file!
method soundness? (line 51) | def soundness?
method reachability_of_final_marking? (line 55) | def reachability_of_final_marking?
method quasiliveness? (line 63) | def quasiliveness?
method deadlock? (line 67) | def deadlock?
method dead_transition? (line 75) | def dead_transition?(transition)
method run_cmd (line 81) | def run_cmd(formula, bucket)
FILE: app/models/wf/multiple_instances/all_finish.rb
type Wf (line 3) | module Wf
type MultipleInstances (line 4) | module MultipleInstances
class AllFinish (line 5) | class AllFinish
method perform (line 6) | def perform(_workitem)
FILE: app/models/wf/party.rb
type Wf (line 15) | module Wf
class Party (line 16) | class Party < ApplicationRecord
FILE: app/models/wf/place.rb
type Wf (line 17) | module Wf
class Place (line 18) | class Place < ApplicationRecord
method graph_id (line 28) | def graph_id
method lola_id (line 32) | def lola_id
FILE: app/models/wf/token.rb
type Wf (line 23) | module Wf
class Token (line 24) | class Token < ApplicationRecord
FILE: app/models/wf/transition.rb
type Wf (line 32) | module Wf
class Transition (line 33) | class Transition < ApplicationRecord
method is_sub_workflow? (line 53) | def is_sub_workflow?
method explicit_or_split? (line 57) | def explicit_or_split?
method validate_trigger_type_and_sub (line 63) | def validate_trigger_type_and_sub
method graph_id (line 67) | def graph_id
method lola_id (line 71) | def lola_id
FILE: app/models/wf/transition_static_assignment.rb
type Wf (line 15) | module Wf
class TransitionStaticAssignment (line 16) | class TransitionStaticAssignment < ApplicationRecord
FILE: app/models/wf/user.rb
type Wf (line 14) | module Wf
class User (line 15) | class User < ApplicationRecord
FILE: app/models/wf/workflow.rb
type Wf (line 16) | module Wf
class Workflow (line 17) | class Workflow < ApplicationRecord
method do_validate! (line 40) | def do_validate!
method to_graph (line 75) | def to_graph(wf_case = nil, base = nil)
method render_graph (line 190) | def render_graph(wf_case = nil)
method to_lola (line 197) | def to_lola
method to_rgl (line 201) | def to_rgl
FILE: app/models/wf/workitem.rb
type Wf (line 28) | module Wf
class Workitem (line 29) | class Workitem < ApplicationRecord
method todo (line 51) | def self.todo(wf_current_user)
method doing (line 59) | def self.doing(wf_current_user)
method done (line 63) | def self.done(wf_current_user)
method for_mini_racer (line 67) | def for_mini_racer
method parent? (line 80) | def parent?
method name (line 84) | def name
method pass_guard? (line 88) | def pass_guard?(arc, has_passed = false)
method real? (line 97) | def real?
method started_by? (line 104) | def started_by?(user)
method finished_by? (line 108) | def finished_by?(user)
method owned_by? (line 112) | def owned_by?(user)
FILE: app/models/wf/workitem_assignment.rb
type Wf (line 14) | module Wf
class WorkitemAssignment (line 15) | class WorkitemAssignment < ApplicationRecord
FILE: db/migrate/20200130201043_init.rb
class Init (line 3) | class Init < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200130201641_init_some_data.rb
class InitSomeData (line 3) | class InitSomeData < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200131200455_create_wf_entries.rb
class CreateWfEntries (line 3) | class CreateWfEntries < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200201001543_add_target_field_name_for_guard.rb
class AddTargetFieldNameForGuard (line 3) | class AddTargetFieldNameForGuard < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200212120019_remove_targetable_from_workitem.rb
class RemoveTargetableFromWorkitem (line 3) | class RemoveTargetableFromWorkitem < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200213085258_add_formable.rb
class AddFormable (line 3) | class AddFormable < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200213125753_add_form_id_for_entry.rb
class AddFormIdForEntry (line 3) | class AddFormIdForEntry < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200213130900_remove_workflow_id_from_form_related.rb
class RemoveWorkflowIdFromFormRelated (line 3) | class RemoveWorkflowIdFromFormRelated < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200220070839_remove_unused_column.rb
class RemoveUnusedColumn (line 3) | class RemoveUnusedColumn < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200220072512_add_sub_workflow.rb
class AddSubWorkflow (line 3) | class AddSubWorkflow < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200222150432_add_multi_instance.rb
class AddMultiInstance (line 3) | class AddMultiInstance < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: db/migrate/20200226195134_add_dynamic_assign_by.rb
class AddDynamicAssignBy (line 3) | class AddDynamicAssignBy < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: lib/wf.rb
type Wf (line 5) | module Wf
FILE: lib/wf/engine.rb
type Wf (line 3) | module Wf
class Engine (line 4) | class Engine < ::Rails::Engine
FILE: lib/wf/version.rb
type Wf (line 3) | module Wf
FILE: test/controllers/wf/arcs_controller_test.rb
type Wf (line 5) | module Wf
class ArcsControllerTest (line 6) | class ArcsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/cases_controller_test.rb
type Wf (line 5) | module Wf
class CasesControllerTest (line 6) | class CasesControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/comments_controller_test.rb
type Wf (line 5) | module Wf
class CommentsControllerTest (line 6) | class CommentsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/fields_controller_test.rb
type Wf (line 5) | module Wf
class FieldsControllerTest (line 6) | class FieldsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/forms_controller_test.rb
type Wf (line 5) | module Wf
class FormsControllerTest (line 6) | class FormsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/guards_controller_test.rb
type Wf (line 5) | module Wf
class GuardsControllerTest (line 6) | class GuardsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/places_controller_test.rb
type Wf (line 5) | module Wf
class PlacesControllerTest (line 6) | class PlacesControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/static_assignments_controller_test.rb
type Wf (line 5) | module Wf
class StaticAssignmentsControllerTest (line 6) | class StaticAssignmentsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/transitions_controller_test.rb
type Wf (line 5) | module Wf
class TransitionsControllerTest (line 6) | class TransitionsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/workflows_controller_test.rb
type Wf (line 5) | module Wf
class WorkflowsControllerTest (line 6) | class WorkflowsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/workitem_assignments_controller_test.rb
type Wf (line 5) | module Wf
class WorkitemAssignmentsControllerTest (line 6) | class WorkitemAssignmentsControllerTest < ActionDispatch::IntegrationTest
FILE: test/controllers/wf/workitems_controller_test.rb
type Wf (line 5) | module Wf
class WorkitemsControllerTest (line 6) | class WorkitemsControllerTest < ActionDispatch::IntegrationTest
FILE: test/dummy/app/channels/application_cable/channel.rb
type ApplicationCable (line 3) | module ApplicationCable
class Channel (line 4) | class Channel < ActionCable::Channel::Base
FILE: test/dummy/app/channels/application_cable/connection.rb
type ApplicationCable (line 3) | module ApplicationCable
class Connection (line 4) | class Connection < ActionCable::Connection::Base
FILE: test/dummy/app/controllers/application_controller.rb
class ApplicationController (line 3) | class ApplicationController < ActionController::Base
method current_user (line 4) | def current_user
FILE: test/dummy/app/helpers/application_helper.rb
type ApplicationHelper (line 3) | module ApplicationHelper
FILE: test/dummy/app/jobs/application_job.rb
class ApplicationJob (line 3) | class ApplicationJob < ActiveJob::Base
FILE: test/dummy/app/mailers/application_mailer.rb
class ApplicationMailer (line 3) | class ApplicationMailer < ActionMailer::Base
FILE: test/dummy/app/models/application_record.rb
class ApplicationRecord (line 3) | class ApplicationRecord < ActiveRecord::Base
FILE: test/dummy/app/models/entry.rb
class Entry (line 15) | class Entry < ApplicationRecord
method json (line 25) | def json
method update_payload! (line 29) | def update_payload!
FILE: test/dummy/app/models/field.rb
class Field (line 18) | class Field < ApplicationRecord
method field_type_for_view (line 43) | def field_type_for_view
method array? (line 62) | def array?
method type_for_cast (line 68) | def type_for_cast
FILE: test/dummy/app/models/field_value.rb
class FieldValue (line 18) | class FieldValue < ApplicationRecord
method value_after_cast (line 23) | def value_after_cast
method value= (line 37) | def value=(v)
method value (line 45) | def value
FILE: test/dummy/app/models/form.rb
class Form (line 14) | class Form < ApplicationRecord
FILE: test/dummy/config/application.rb
type Dummy (line 10) | module Dummy
class Application (line 11) | class Application < Rails::Application
FILE: test/dummy/config/initializers/my_assignment_callback.rb
class MyAssignmentCallback (line 3) | class MyAssignmentCallback
method perform (line 4) | def perform(_workitem_id)
FILE: test/dummy/db/migrate/20200213081814_new_form.rb
class NewForm (line 3) | class NewForm < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: test/dummy/db/migrate/20200213133942_add_form_id_in_entry1.rb
class AddFormIdInEntry1 (line 3) | class AddFormIdInEntry1 < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: test/dummy/db/migrate/20200214005535_add_entry_id_for_field_values1.rb
class AddEntryIdForFieldValues1 (line 3) | class AddEntryIdForFieldValues1 < ActiveRecord::Migration[6.0]
method change (line 4) | def change
FILE: test/integration/navigation_test.rb
class NavigationTest (line 5) | class NavigationTest < ActionDispatch::IntegrationTest
FILE: test/models/wf/case_assignment_test.rb
type Wf (line 18) | module Wf
class CaseAssignmentTest (line 19) | class CaseAssignmentTest < ActiveSupport::TestCase
FILE: test/models/wf/comment_test.rb
type Wf (line 18) | module Wf
class CommentTest (line 19) | class CommentTest < ActiveSupport::TestCase
FILE: test/models/wf/demo_target_test.rb
type Wf (line 16) | module Wf
class DemoTargetTest (line 17) | class DemoTargetTest < ActiveSupport::TestCase
FILE: test/models/wf/entry_test.rb
type Wf (line 18) | module Wf
class EntryTest (line 19) | class EntryTest < ActiveSupport::TestCase
FILE: test/models/wf/field_test.rb
type Wf (line 20) | module Wf
class FieldTest (line 21) | class FieldTest < ActiveSupport::TestCase
FILE: test/models/wf/field_value_test.rb
type Wf (line 18) | module Wf
class FieldValueTest (line 19) | class FieldValueTest < ActiveSupport::TestCase
FILE: test/models/wf/form_test.rb
type Wf (line 16) | module Wf
class FormTest (line 17) | class FormTest < ActiveSupport::TestCase
FILE: test/models/wf/guard_test.rb
type Wf (line 21) | module Wf
class GuardTest (line 22) | class GuardTest < ActiveSupport::TestCase
FILE: test/models/wf/party_test.rb
type Wf (line 17) | module Wf
class PartyTest (line 18) | class PartyTest < ActiveSupport::TestCase
FILE: test/models/wf/transition_static_assignment_test.rb
type Wf (line 17) | module Wf
class TransitionStaticAssignmentTest (line 18) | class TransitionStaticAssignmentTest < ActiveSupport::TestCase
FILE: test/models/wf/user_test.rb
type Wf (line 16) | module Wf
class UserTest (line 17) | class UserTest < ActiveSupport::TestCase
FILE: test/models/wf/wf_test.rb
type Wf (line 5) | module Wf
class WfTest (line 6) | class WfTest < ActiveSupport::TestCase
FILE: test/models/wf/workitem_assignment_test.rb
type Wf (line 16) | module Wf
class WorkitemAssignmentTest (line 17) | class WorkitemAssignmentTest < ActiveSupport::TestCase
Condensed preview — 294 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (280K chars).
[
{
"path": ".editorconfig",
"chars": 243,
"preview": "# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\n\nroot = true\n\n[*]\ncharset"
},
{
"path": ".gitattributes",
"chars": 36,
"preview": "*.rb diff=ruby\n*.gemspec diff=ruby\n\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1170,
"preview": "name: Testing\n\non: [push, pull_request]\n\njobs:\n build:\n\n runs-on: ubuntu-latest\n\n services:\n db:\n ima"
},
{
"path": ".github/workflows/gempush.yml",
"chars": 628,
"preview": "name: Ruby Gem\n\non:\n push:\n tags:\n - v*\n\njobs:\n build:\n name: Build + Publish\n runs-on: ubuntu-latest\n\n "
},
{
"path": ".gitignore",
"chars": 829,
"preview": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring t"
},
{
"path": ".rubocop.yml",
"chars": 4798,
"preview": "require:\n - rubocop-performance\n - rubocop-rails\n\nAllCops:\n TargetRubyVersion: 2.5\n Exclude:\n - bin/**/*\n - te"
},
{
"path": "FormSpec.md",
"chars": 1162,
"preview": "## Why\n\nThe core of Petri Flow is the workflow engine. \nHowever, workflow, dynamic forms, and organization systems are i"
},
{
"path": "Gemfile",
"chars": 933,
"preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.gi"
},
{
"path": "Guard.md",
"chars": 3091,
"preview": "## Guard Expression\n\nThere are two per-defined variables for your guard expression, `workitem`, `target`.\n\nSchema for `w"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2020 Hooopo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "MIT-LICENSE",
"chars": 1051,
"preview": "Copyright 2020 Hooopo Wang\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this softwar"
},
{
"path": "README.md",
"chars": 3409,
"preview": "# Petri Flow  ;\n border: rgb(94,94,94);\n}\n"
},
{
"path": "app/assets/stylesheets/wf/uikit/card.scss",
"chars": 150,
"preview": ".card{\n margin-bottom: 4rem;\n border: 1px solid rgba(230, 230, 230, 0.41);\n border-radius: 0.625rem;\n box-shadow: 0 "
},
{
"path": "app/assets/stylesheets/wf/uikit/index.scss",
"chars": 142,
"preview": "@import \"_variables\";\n@import \"bootstrap\";\n\n//overwrite\n@import \"navbar\";\n@import \"table\";\n@import \"card\";\n@import \"aler"
},
{
"path": "app/assets/stylesheets/wf/uikit/navbar.scss",
"chars": 524,
"preview": "@import \"variables\";\n\n.navbar-petri{\n box-shadow: 0 12px 24px 0 rgba(0,0,0,0.05);\n margin-bottom: 2rem;\n background: "
},
{
"path": "app/assets/stylesheets/wf/uikit/table.scss",
"chars": 275,
"preview": "@import \"variables\";\n\n.table-view{\n thead th{\n color: $gray-7;\n font-weight: 500;\n border-top: 0;\n border-b"
},
{
"path": "app/assets/stylesheets/wf/workflows.css",
"chars": 128,
"preview": "/*\n Place all the styles related to the matching controller here.\n They will automatically be included in application."
},
{
"path": "app/assets/stylesheets/wf/workitem_assignments.css",
"chars": 128,
"preview": "/*\n Place all the styles related to the matching controller here.\n They will automatically be included in application."
},
{
"path": "app/assets/stylesheets/wf/workitems.css",
"chars": 128,
"preview": "/*\n Place all the styles related to the matching controller here.\n They will automatically be included in application."
},
{
"path": "app/controllers/wf/application_controller.rb",
"chars": 271,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class ApplicationController < ::ApplicationController\n protect_from_forger"
},
{
"path": "app/controllers/wf/arcs_controller.rb",
"chars": 1665,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class ArcsController < Applic"
},
{
"path": "app/controllers/wf/cases_controller.rb",
"chars": 1503,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class CasesController < Appli"
},
{
"path": "app/controllers/wf/comments_controller.rb",
"chars": 987,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class CommentsController < Ap"
},
{
"path": "app/controllers/wf/fields_controller.rb",
"chars": 1348,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class FieldsController < Appl"
},
{
"path": "app/controllers/wf/forms_controller.rb",
"chars": 1242,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class FormsController < Appli"
},
{
"path": "app/controllers/wf/guards_controller.rb",
"chars": 1879,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class GuardsController < Appl"
},
{
"path": "app/controllers/wf/places_controller.rb",
"chars": 1465,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class PlacesController < Appl"
},
{
"path": "app/controllers/wf/static_assignments_controller.rb",
"chars": 1113,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class StaticAssignmentsContro"
},
{
"path": "app/controllers/wf/transitions_controller.rb",
"chars": 2321,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class TransitionsController <"
},
{
"path": "app/controllers/wf/workflows_controller.rb",
"chars": 1423,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class WorkflowsController < A"
},
{
"path": "app/controllers/wf/workitem_assignments_controller.rb",
"chars": 1151,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class WorkitemAssignmentsCont"
},
{
"path": "app/controllers/wf/workitems_controller.rb",
"chars": 3451,
"preview": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n class WorkitemsController < A"
},
{
"path": "app/helpers/wf/application_helper.rb",
"chars": 78,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module ApplicationHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/arcs_helper.rb",
"chars": 71,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module ArcsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/cases_helper.rb",
"chars": 72,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module CasesHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/comments_helper.rb",
"chars": 75,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module CommentsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/fields_helper.rb",
"chars": 73,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module FieldsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/forms_helper.rb",
"chars": 72,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module FormsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/guards_helper.rb",
"chars": 73,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module GuardsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/places_helper.rb",
"chars": 73,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module PlacesHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/static_assignments_helper.rb",
"chars": 84,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module StaticAssignmentsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/transitions_helper.rb",
"chars": 78,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module TransitionsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/workflows_helper.rb",
"chars": 76,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module WorkflowsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/workitem_assignments_helper.rb",
"chars": 86,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module WorkitemAssignmentsHelper\n end\nend\n"
},
{
"path": "app/helpers/wf/workitems_helper.rb",
"chars": 76,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module WorkitemsHelper\n end\nend\n"
},
{
"path": "app/jobs/wf/application_job.rb",
"chars": 92,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class ApplicationJob < ActiveJob::Base\n end\nend\n"
},
{
"path": "app/jobs/wf/fire_timed_workitem_job.rb",
"chars": 394,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class FireTimedWorkitemJob < ApplicationJob\n queue_as :default\n\n def pe"
},
{
"path": "app/mailers/wf/application_mailer.rb",
"chars": 155,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class ApplicationMailer < ActionMailer::Base\n default from: \"from@example."
},
{
"path": "app/models/wf/acts_as_party.rb",
"chars": 509,
"preview": "# frozen_string_literal: true\n\nrequire \"active_support/concern\"\n\nmodule Wf\n module ActsAsParty\n extend ActiveSupport"
},
{
"path": "app/models/wf/application_record.rb",
"chars": 129,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class ApplicationRecord < ActiveRecord::Base\n self.abstract_class = true\n "
},
{
"path": "app/models/wf/arc.rb",
"chars": 963,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_arcs\n#\n# id :integer not "
},
{
"path": "app/models/wf/callbacks/assignment_default.rb",
"chars": 203,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class AssignmentDefault < ApplicationJob\n queue_as :default\n\n "
},
{
"path": "app/models/wf/callbacks/deadline_default.rb",
"chars": 194,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class DeadlineDefault < ApplicationJob\n queue_as :default\n\n "
},
{
"path": "app/models/wf/callbacks/enable_default.rb",
"chars": 192,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class EnableDefault < ApplicationJob\n queue_as :default\n\n de"
},
{
"path": "app/models/wf/callbacks/fire_default.rb",
"chars": 190,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class FireDefault < ApplicationJob\n queue_as :default\n\n def "
},
{
"path": "app/models/wf/callbacks/hold_timeout_default.rb",
"chars": 197,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class HoldTimeoutDefault < ApplicationJob\n queue_as :default\n\n "
},
{
"path": "app/models/wf/callbacks/notification_default.rb",
"chars": 198,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class NotificationDefault < ApplicationJob\n queue_as :default\n\n"
},
{
"path": "app/models/wf/callbacks/time_default.rb",
"chars": 190,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class TimeDefault < ApplicationJob\n queue_as :default\n\n def "
},
{
"path": "app/models/wf/callbacks/unassignment_default.rb",
"chars": 198,
"preview": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n class UnassignmentDefault < ApplicationJob\n queue_as :default\n\n"
},
{
"path": "app/models/wf/case.rb",
"chars": 1171,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_cases\n#\n# id :integer "
},
{
"path": "app/models/wf/case_assignment.rb",
"chars": 482,
"preview": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_case_assignments\n#\n# id :integer "
},
{
"path": "app/models/wf/case_command/add_comment.rb",
"chars": 367,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class AddComment\n prepend SimpleCommand\n attr_reader :work"
},
{
"path": "app/models/wf/case_command/add_manual_assignment.rb",
"chars": 406,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class AddManualAssignment\n prepend SimpleCommand\n attr_rea"
},
{
"path": "app/models/wf/case_command/add_token.rb",
"chars": 368,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class AddToken\n prepend SimpleCommand\n attr_reader :wf_cas"
},
{
"path": "app/models/wf/case_command/add_workitem_assignment.rb",
"chars": 1707,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class AddWorkitemAssignment\n prepend SimpleCommand\n attr_r"
},
{
"path": "app/models/wf/case_command/begin_workitem_action.rb",
"chars": 1256,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class BeginWorkitemAction\n prepend SimpleCommand\n attr_rea"
},
{
"path": "app/models/wf/case_command/cancel.rb",
"chars": 345,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class Cancel\n prepend SimpleCommand\n attr_reader :wf_case\n"
},
{
"path": "app/models/wf/case_command/cancel_workitem.rb",
"chars": 527,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class CancelWorkitem\n prepend SimpleCommand\n attr_reader :"
},
{
"path": "app/models/wf/case_command/clear_manual_assignments.rb",
"chars": 367,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class ClearManualAssignments\n prepend SimpleCommand\n attr_"
},
{
"path": "app/models/wf/case_command/clear_workitem_assignments.rb",
"chars": 568,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class ClearWorkitemAssignments\n prepend SimpleCommand\n att"
},
{
"path": "app/models/wf/case_command/consume_token.rb",
"chars": 724,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class ConsumeToken\n prepend SimpleCommand\n attr_reader :wf"
},
{
"path": "app/models/wf/case_command/create_entry.rb",
"chars": 1005,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class CreateEntry\n prepend SimpleCommand\n attr_reader :for"
},
{
"path": "app/models/wf/case_command/enable_transitions.rb",
"chars": 1508,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class EnableTransitions\n prepend SimpleCommand\n attr_reade"
},
{
"path": "app/models/wf/case_command/end_workitem_action.rb",
"chars": 615,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class EndWorkitemAction\n prepend SimpleCommand\n attr_reade"
},
{
"path": "app/models/wf/case_command/finish_workitem.rb",
"chars": 1095,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class FinishWorkitem\n prepend SimpleCommand\n attr_reader :"
},
{
"path": "app/models/wf/case_command/finished_p.rb",
"chars": 1006,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class FinishedP\n prepend SimpleCommand\n attr_reader :wf_ca"
},
{
"path": "app/models/wf/case_command/fire_message_transition.rb",
"chars": 501,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class FireMessageTransition\n prepend SimpleCommand\n attr_r"
},
{
"path": "app/models/wf/case_command/fire_transition_internal.rb",
"chars": 1259,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class FireTransitionInternal\n prepend SimpleCommand\n attr_"
},
{
"path": "app/models/wf/case_command/lock_token.rb",
"chars": 473,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class LockToken\n prepend SimpleCommand\n attr_reader :wf_ca"
},
{
"path": "app/models/wf/case_command/new.rb",
"chars": 445,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class New\n prepend SimpleCommand\n attr_reader :workflow, :"
},
{
"path": "app/models/wf/case_command/release_token.rb",
"chars": 487,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class ReleaseToken\n prepend SimpleCommand\n attr_reader :wo"
},
{
"path": "app/models/wf/case_command/remove_manual_assignment.rb",
"chars": 417,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class RemoveManualAssignment\n prepend SimpleCommand\n attr_"
},
{
"path": "app/models/wf/case_command/remove_workitem_assignment.rb",
"chars": 715,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class RemoveWorkitemAssignment\n prepend SimpleCommand\n att"
},
{
"path": "app/models/wf/case_command/resume.rb",
"chars": 346,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class Resume\n prepend SimpleCommand\n attr_reader :wf_case\n"
},
{
"path": "app/models/wf/case_command/set_workitem_assignments.rb",
"chars": 1059,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class SetWorkitemAssignments\n prepend SimpleCommand\n attr_"
},
{
"path": "app/models/wf/case_command/start_case.rb",
"chars": 409,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class StartCase\n prepend SimpleCommand\n attr_reader :wf_ca"
},
{
"path": "app/models/wf/case_command/start_workitem.rb",
"chars": 703,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class StartWorkitem\n prepend SimpleCommand\n attr_reader :w"
},
{
"path": "app/models/wf/case_command/suspend.rb",
"chars": 325,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class Suspend\n prepend SimpleCommand\n attr_reader :wf_case"
},
{
"path": "app/models/wf/case_command/sweep_automatic_transitions.rb",
"chars": 857,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class SweepAutomaticTransitions\n prepend SimpleCommand\n at"
},
{
"path": "app/models/wf/case_command/sweep_timed_transitions.rb",
"chars": 394,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class SweepTimedTransitions\n prepend SimpleCommand\n\n def c"
},
{
"path": "app/models/wf/case_command/workitem_action.rb",
"chars": 477,
"preview": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n class WorkitemAction\n prepend SimpleCommand\n attr_reader :"
},
{
"path": "app/models/wf/comment.rb",
"chars": 459,
"preview": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_comments\n#\n# id :integer not"
},
{
"path": "app/models/wf/demo_target.rb",
"chars": 373,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_demo_targets\n#\n# id :integer "
},
{
"path": "app/models/wf/entry.rb",
"chars": 951,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_entries\n#\n# id :integer not"
},
{
"path": "app/models/wf/field.rb",
"chars": 1658,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_fields\n#\n# id :integer "
},
{
"path": "app/models/wf/field_value.rb",
"chars": 914,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_field_values\n#\n# id :integer "
},
{
"path": "app/models/wf/form.rb",
"chars": 385,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_forms\n#\n# id :integer not n"
},
{
"path": "app/models/wf/group.rb",
"chars": 396,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_groups\n#\n# id :integer not n"
},
{
"path": "app/models/wf/guard.rb",
"chars": 2436,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_guards\n#\n# id :integer n"
},
{
"path": "app/models/wf/lola.rb",
"chars": 2524,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class Lola\n attr_reader :end_p, :start_p, :workflow\n def initialize(wor"
},
{
"path": "app/models/wf/multiple_instances/all_finish.rb",
"chars": 159,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n module MultipleInstances\n class AllFinish\n def perform(_workitem)\n "
},
{
"path": "app/models/wf/party.rb",
"chars": 557,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_parties\n#\n# id :integer n"
},
{
"path": "app/models/wf/place.rb",
"chars": 680,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_places\n#\n# id :integer not "
},
{
"path": "app/models/wf/token.rb",
"chars": 903,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_tokens\n#\n# id :integer "
},
{
"path": "app/models/wf/transition.rb",
"chars": 2659,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_transitions\n#\n# id :intege"
},
{
"path": "app/models/wf/transition_static_assignment.rb",
"chars": 643,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_transition_static_assignments\n#\n# id "
},
{
"path": "app/models/wf/user.rb",
"chars": 434,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_users\n#\n# id :integer not nu"
},
{
"path": "app/models/wf/workflow.rb",
"chars": 7378,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workflows\n#\n# id :integer n"
},
{
"path": "app/models/wf/workitem.rb",
"chars": 3992,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workitems\n#\n# id :intege"
},
{
"path": "app/models/wf/workitem_assignment.rb",
"chars": 401,
"preview": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workitem_assignments\n#\n# id :integer"
},
{
"path": "app/views/layouts/wf/_alert.html.erb",
"chars": 507,
"preview": "<% return if alert.blank? %>\n<div id=\"alert\" class=\"alert alert-warning\">\n <p>\n <svg viewBox=\"64 64 896 896\" focusab"
},
{
"path": "app/views/layouts/wf/_footer.html.erb",
"chars": 428,
"preview": "<footer class=\"footer\">\n <div class=\"container\">\n <p>\n <strong>\n <a href=\"https://github.com/hooopo/petr"
},
{
"path": "app/views/layouts/wf/_nav.html.erb",
"chars": 573,
"preview": "<nav class=\"navbar navbar-petri\" role=\"navigation\" aria-label=\"main navigation\">\n <div class=\"container\">\n <div clas"
},
{
"path": "app/views/layouts/wf/_notice.html.erb",
"chars": 551,
"preview": "<% return if notice.blank? %>\n<div id=\"notice\" class=\"alert alert-success\">\n <p>\n <svg viewBox=\"64 64 896 896\" focus"
},
{
"path": "app/views/layouts/wf/application.html.erb",
"chars": 1072,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>Petri Flow</title>\n\n <%= csrf_meta_tags %>\n <meta http-equiv=\"Content"
},
{
"path": "app/views/wf/arcs/_form.html.erb",
"chars": 1417,
"preview": "<%= form_with(model: arc, url: [@workflow, @arc], local: true) do |f| %>\n <% if arc.errors.any? %>\n <article class=\""
},
{
"path": "app/views/wf/arcs/edit.html.erb",
"chars": 89,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, arc: @arc %>\n</div>\n"
},
{
"path": "app/views/wf/arcs/new.html.erb",
"chars": 89,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, arc: @arc %>\n</div>\n"
},
{
"path": "app/views/wf/arcs/show.html.erb",
"chars": 2129,
"preview": "<div class=\"card card-body\">\n<div class=\"float-right\">\n <%= link_to 'Delete Arc', workflow_path(@workflow, @arc), data:"
},
{
"path": "app/views/wf/cases/_form.html.erb",
"chars": 963,
"preview": "<%= form_with(model: wf_case, url: [@workflow, @wf_case], local: true) do |f| %>\n <% if wf_case.errors.any? %>\n <art"
},
{
"path": "app/views/wf/cases/index.html.erb",
"chars": 1101,
"preview": "<div class=\"card card-body\">\n <div class=\"float-left\"><h2>Cases</h2></div>\n <div class=\"float-right\">\n <%= link_to "
},
{
"path": "app/views/wf/cases/new.html.erb",
"chars": 97,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, wf_case: @wf_case %>\n</div>\n"
},
{
"path": "app/views/wf/cases/show.html.erb",
"chars": 3364,
"preview": "<div class=\"card card-body\">\n<div>\n <h2>Case Detail</h2>\n <table class=\"table table-view\">\n <tbody>\n <tr>\n "
},
{
"path": "app/views/wf/comments/new.html.erb",
"chars": 913,
"preview": "<div class=\"card card-body\">\n<%= form_with(model: @comment, url: workitem_comments_path(@workitem), local: true) do |f| "
},
{
"path": "app/views/wf/fields/_form.html.erb",
"chars": 1369,
"preview": "<%= form_with(model: field, url: [@form, field], local: true) do |f| %>\n <% if field.errors.any? %>\n <article class="
},
{
"path": "app/views/wf/fields/edit.html.erb",
"chars": 85,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", form: @form, field: @field %>\n</div>\n"
},
{
"path": "app/views/wf/fields/new.html.erb",
"chars": 85,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", form: @form, field: @field %>\n</div>\n"
},
{
"path": "app/views/wf/forms/_form.html.erb",
"chars": 960,
"preview": "<%= form_with(model: form, local: true) do |f| %>\n <% if form.errors.any? %>\n <article class=\"message is-danger\">\n "
},
{
"path": "app/views/wf/forms/edit.html.erb",
"chars": 70,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", form: @form %>\n</div>\n"
},
{
"path": "app/views/wf/forms/index.html.erb",
"chars": 1051,
"preview": "<div class=\"card card-body\">\n <div class=\"d-flex justify-content-between\">\n <div><h2>Forms</h2></div>\n <div><%= lin"
},
{
"path": "app/views/wf/forms/new.html.erb",
"chars": 70,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", form: @form %>\n</div>\n"
},
{
"path": "app/views/wf/forms/show.html.erb",
"chars": 1720,
"preview": "<div class=\"card card-body\">\n<div class=\"d-flex justify-content-end\">\n <%= link_to 'Delete Form', form_path(@form), da"
},
{
"path": "app/views/wf/guards/_form.html.erb",
"chars": 1646,
"preview": "<%= form_with(model: guard, url: [@arc, @guard], local: true) do |f| %>\n <% if guard.errors.any? %>\n <article class="
},
{
"path": "app/views/wf/guards/edit.html.erb",
"chars": 83,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", arc: @arc, guard: @guard %>\n</div>\n"
},
{
"path": "app/views/wf/guards/new.html.erb",
"chars": 83,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", arc: @arc, guard: @guard %>\n</div>\n"
},
{
"path": "app/views/wf/places/_form.html.erb",
"chars": 1374,
"preview": "<%= form_with(model: place, url: [@workflow, @place], local: true) do |f| %>\n <% if place.errors.any? %>\n <article c"
},
{
"path": "app/views/wf/places/edit.html.erb",
"chars": 93,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, place: @place %>\n</div>\n"
},
{
"path": "app/views/wf/places/new.html.erb",
"chars": 93,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, place: @place %>\n</div>\n"
},
{
"path": "app/views/wf/static_assignments/_form.html.erb",
"chars": 1028,
"preview": "<%= form_with(model: static_assignment, url: transition_static_assignments_path(static_assignment.transition), local: tr"
},
{
"path": "app/views/wf/static_assignments/new.html.erb",
"chars": 98,
"preview": "<div class=\"card card-body\">\n <%= render \"form\", static_assignment: @static_assignment %>\n</div>\n"
},
{
"path": "app/views/wf/transitions/_form.html.erb",
"chars": 5510,
"preview": "<%= form_with(model: transition, url: [@workflow, @transition], local: true) do |f| %>\n <% if transition.errors.any? %>"
},
{
"path": "app/views/wf/transitions/edit.html.erb",
"chars": 103,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, transition: @transition %>\n</div>\n"
},
{
"path": "app/views/wf/transitions/new.html.erb",
"chars": 103,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, transition: @transition %>\n</div>\n"
},
{
"path": "app/views/wf/transitions/show.html.erb",
"chars": 2720,
"preview": "<div class=\"card card-body\">\n<div class=\"float-right\">\n <%= link_to 'Edit Transition', edit_workflow_transition_path(@w"
},
{
"path": "app/views/wf/workflows/_form.html.erb",
"chars": 980,
"preview": "<%= form_with(model: workflow, local: true) do |f| %>\n <% if workflow.errors.any? %>\n <article class=\"message is-dan"
},
{
"path": "app/views/wf/workflows/edit.html.erb",
"chars": 78,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow %>\n</div>\n"
},
{
"path": "app/views/wf/workflows/index.html.erb",
"chars": 1580,
"preview": "<div class=\"card card-body\">\n <div class=\"d-flex justify-content-between\">\n\n <div><h2>Workflows</h2></div>\n<div>\n <%"
},
{
"path": "app/views/wf/workflows/new.html.erb",
"chars": 78,
"preview": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow %>\n</div>\n"
},
{
"path": "app/views/wf/workflows/show.html.erb",
"chars": 6866,
"preview": "<div class=\"d-flex justify-content-end\">\n<% if @workflow.is_valid? %>\n <%= link_to 'New Case', new_workflow_case_pat"
},
{
"path": "app/views/wf/workitem_assignments/new.html.erb",
"chars": 1044,
"preview": "<div class=\"card card-body\">\n<%= form_with(model: @workitem_assignment, url: workitem_workitem_assignments_path(@workite"
},
{
"path": "app/views/wf/workitems/index.html.erb",
"chars": 2815,
"preview": "<div class=\"card card-body\">\n<div>\n <h2 class=\"\">Stats</h2>\n <table class=\"table table-view\">\n <thead>\n <tr>\n "
},
{
"path": "app/views/wf/workitems/pre_finish.html.erb",
"chars": 1777,
"preview": "<%= form_with(model: @workitem, url: finish_workitem_path(@workitem), method: :put, local: true) do |f| %>\n <% if @work"
},
{
"path": "app/views/wf/workitems/show.html.erb",
"chars": 7101,
"preview": "<div class=\"card card-body\">\n<div class=\"float-right\">\n <%= link_to 'Back to Case', workflow_case_path(@workitem.workfl"
},
{
"path": "bin/rails",
"chars": 544,
"preview": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails gems\n# installed from the r"
},
{
"path": "config/routes.rb",
"chars": 615,
"preview": "# frozen_string_literal: true\n\nWf::Engine.routes.draw do\n resources :workflows do\n resources :transitions\n resour"
},
{
"path": "db/migrate/20200130201043_init.rb",
"chars": 9718,
"preview": "# frozen_string_literal: true\n\nclass Init < ActiveRecord::Migration[6.0]\n def change\n create_table \"wf_arcs\", force:"
},
{
"path": "db/migrate/20200130201641_init_some_data.rb",
"chars": 336,
"preview": "# frozen_string_literal: true\n\nclass InitSomeData < ActiveRecord::Migration[6.0]\n def change\n 5.times do |i|\n W"
},
{
"path": "db/migrate/20200131200455_create_wf_entries.rb",
"chars": 688,
"preview": "# frozen_string_literal: true\n\nclass CreateWfEntries < ActiveRecord::Migration[6.0]\n def change\n create_table :wf_en"
},
{
"path": "db/migrate/20200201001543_add_target_field_name_for_guard.rb",
"chars": 225,
"preview": "# frozen_string_literal: true\n\nclass AddTargetFieldNameForGuard < ActiveRecord::Migration[6.0]\n def change\n add_colu"
},
{
"path": "db/migrate/20200212120019_remove_targetable_from_workitem.rb",
"chars": 218,
"preview": "# frozen_string_literal: true\n\nclass RemoveTargetableFromWorkitem < ActiveRecord::Migration[6.0]\n def change\n remove"
},
{
"path": "db/migrate/20200213085258_add_formable.rb",
"chars": 229,
"preview": "# frozen_string_literal: true\n\nclass AddFormable < ActiveRecord::Migration[6.0]\n def change\n add_column :wf_transiti"
},
{
"path": "db/migrate/20200213125753_add_form_id_for_entry.rb",
"chars": 168,
"preview": "# frozen_string_literal: true\n\nclass AddFormIdForEntry < ActiveRecord::Migration[6.0]\n def change\n add_column :wf_en"
},
{
"path": "db/migrate/20200213130900_remove_workflow_id_from_form_related.rb",
"chars": 223,
"preview": "# frozen_string_literal: true\n\nclass RemoveWorkflowIdFromFormRelated < ActiveRecord::Migration[6.0]\n def change\n rem"
},
{
"path": "db/migrate/20200220070839_remove_unused_column.rb",
"chars": 158,
"preview": "# frozen_string_literal: true\n\nclass RemoveUnusedColumn < ActiveRecord::Migration[6.0]\n def change\n remove_column :w"
},
{
"path": "db/migrate/20200220072512_add_sub_workflow.rb",
"chars": 320,
"preview": "# frozen_string_literal: true\n\nclass AddSubWorkflow < ActiveRecord::Migration[6.0]\n def change\n add_column :wf_trans"
},
{
"path": "db/migrate/20200222150432_add_multi_instance.rb",
"chars": 675,
"preview": "# frozen_string_literal: true\n\nclass AddMultiInstance < ActiveRecord::Migration[6.0]\n def change\n add_column :wf_tra"
},
{
"path": "db/migrate/20200226195134_add_dynamic_assign_by.rb",
"chars": 233,
"preview": "# frozen_string_literal: true\n\nclass AddDynamicAssignBy < ActiveRecord::Migration[6.0]\n def change\n add_column :wf_t"
},
{
"path": "lib/tasks/wf_tasks.rake",
"chars": 607,
"preview": "# frozen_string_literal: true\n\ndesc \"Wf tasks\"\n\ntask wf: :environment do\n url = \"http://service-technology.org/files/lo"
},
{
"path": "lib/wf/engine.rb",
"chars": 760,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n class Engine < ::Rails::Engine\n isolate_namespace Wf\n config.autoload_p"
},
{
"path": "lib/wf/version.rb",
"chars": 65,
"preview": "# frozen_string_literal: true\n\nmodule Wf\n VERSION = \"0.2.5\"\nend\n"
},
{
"path": "lib/wf.rb",
"chars": 1549,
"preview": "# frozen_string_literal: true\n\nrequire \"wf/engine\"\n\nmodule Wf\n class << self\n attr_accessor :enable_callbacks\n at"
},
{
"path": "lola.md",
"chars": 2261,
"preview": "## LoLA\n\nLoLA is a Petri nets model-checking tool. To install LoLA, download lola-2.0.tar.gz from http://home.gna.org/se"
},
{
"path": "screenshots/.keep",
"chars": 0,
"preview": ""
},
{
"path": "test/controllers/wf/arcs_controller_test.rb",
"chars": 230,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class ArcsControllerTest < ActionDispatch::Integration"
},
{
"path": "test/controllers/wf/cases_controller_test.rb",
"chars": 231,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class CasesControllerTest < ActionDispatch::Integratio"
},
{
"path": "test/controllers/wf/comments_controller_test.rb",
"chars": 234,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class CommentsControllerTest < ActionDispatch::Integra"
},
{
"path": "test/controllers/wf/fields_controller_test.rb",
"chars": 232,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class FieldsControllerTest < ActionDispatch::Integrati"
},
{
"path": "test/controllers/wf/forms_controller_test.rb",
"chars": 231,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class FormsControllerTest < ActionDispatch::Integratio"
},
{
"path": "test/controllers/wf/guards_controller_test.rb",
"chars": 232,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class GuardsControllerTest < ActionDispatch::Integrati"
},
{
"path": "test/controllers/wf/places_controller_test.rb",
"chars": 232,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class PlacesControllerTest < ActionDispatch::Integrati"
},
{
"path": "test/controllers/wf/static_assignments_controller_test.rb",
"chars": 243,
"preview": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n class StaticAssignmentsControllerTest < ActionDispatch"
}
]
// ... and 94 more files (download for full content)
About this extraction
This page contains the full source code of the hooopo/petri_flow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 294 files (242.8 KB), approximately 76.4k tokens, and a symbol index with 493 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.