[
  {
    "path": ".editorconfig",
    "content": "# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\nend_of_line = lf\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.rb diff=ruby\n*.gemspec diff=ruby\n\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Testing\n\non: [push, pull_request]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    services:\n      db:\n        image: postgres:11\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_DB: postgres\n        ports: ['5432:5432']\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Ruby 2.6\n      uses: actions/setup-ruby@v1\n      with:\n        ruby-version: \"2.6\"\n    - name: Build and test with Rake\n      env:\n        DATABASE_URL: \"postgresql://postgres:postgres@127.0.0.1:5432/postgres\"\n        POSTGRES_USER: postgres\n        POSTGRES_PASSWORD: postgres\n        POSTGRES_DB: postgres\n        RAILS_ENV: test\n      run: |\n        sudo apt-get -yqq install libpq-dev\n        sudo apt-get install libmysqlclient-dev\n        sudo apt-get install graphviz\n        gem install bundler\n        bundle install --jobs 4 --retry 3\n        bundle exec rake app:wf\n        bundle exec rails app:db:create && bundle exec rails app:db:migrate  && bundle exec rails test\n"
  },
  {
    "path": ".github/workflows/gempush.yml",
    "content": "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    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Ruby 2.6\n      uses: actions/setup-ruby@v1\n      with:\n        ruby-version: \"2.6.x\"\n\n    - name: Publish to RubyGems\n      run: |\n        mkdir -p $HOME/.gem\n        touch $HOME/.gem/credentials\n        chmod 0600 $HOME/.gem/credentials\n        printf -- \"---\\n:rubygems_api_key: ${GEM_HOST_API_KEY}\\n\" > $HOME/.gem/credentials\n        gem build *.gemspec\n        gem push *.gem\n      env:\n        GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.idea\n.bundle/\n\n# Ignore the default SQLite database.\ntest/dummy/db/*.sqlite3\ntest/dummy/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\nlog/*.log\n!test/dummy/log/.keep\n!test/dummy/tmp/.keep\ntest/dummy/log/*.log\ntest/dummy/tmp/\n\n# Ignore uploaded files in development\ntest/dummy/storage/*\n!test/dummy/storage/.keep\n\n# Ignore compiled mruby in dummy app\n/test/dummy/mruby/bin\n\npkg/\n.byebug_history\n\nnode_modules/\ntest/dummy/public/packs\ntest/dummy/node_modules/\nyarn-error.log\n\n*.gem\n\n.env\n"
  },
  {
    "path": ".rubocop.yml",
    "content": "require:\n  - rubocop-performance\n  - rubocop-rails\n\nAllCops:\n  TargetRubyVersion: 2.5\n  Exclude:\n    - bin/**/*\n    - test/dummy/bin/**/*\n    - test/dummy/db/schema.rb\n\nRails:\n  Enabled: true\n\nLayout/LineLength:\n  Max: 150\n\nMetrics/MethodLength:\n  Max: 100\n\nMetrics/BlockLength:\n  Max: 50\n\nMetrics/ClassLength:\n  Enabled: false\n\nStyle/GuardClause:\n  Enabled: false\n\nStyle/Documentation:\n  Enabled: false\n\nStyle/ClassAndModuleChildren:\n  Enabled: false\n\nNaming/AccessorMethodName:\n  Enabled: false\n\nNaming/MemoizedInstanceVariableName:\n  Enabled: false\n\n# Prefer assert_not over assert !\nRails/AssertNot:\n  Include:\n    - 'test/**/*'\n\n# Prefer assert_not_x over refute_x\nRails/RefuteMethods:\n  Include:\n    - 'test/**/*'\n\n# Prefer &&/|| over and/or.\nStyle/AndOr:\n  Enabled: true\n\n# Do not use braces for hash literals when they are the last argument of a\n# method call.\nStyle/BracesAroundHashParameters:\n  Enabled: true\n  EnforcedStyle: context_dependent\n\n# Align `when` with `case`.\nLayout/CaseIndentation:\n  Enabled: true\n\n# Align comments with method definitions.\nLayout/CommentIndentation:\n  Enabled: true\n\nLayout/ElseAlignment:\n  Enabled: true\n\n# Align `end` with the matching keyword or starting expression except for\n# assignments, where it should be aligned with the LHS.\nLayout/EndAlignment:\n  Enabled: true\n  EnforcedStyleAlignWith: variable\n  AutoCorrect: true\n\nLayout/EmptyLineAfterMagicComment:\n  Enabled: true\n\nLayout/EmptyLinesAroundBlockBody:\n  Enabled: true\n\n# In a regular class definition, no empty lines around the body.\nLayout/EmptyLinesAroundClassBody:\n  Enabled: true\n\n# In a regular method definition, no empty lines around the body.\nLayout/EmptyLinesAroundMethodBody:\n  Enabled: true\n\n# In a regular module definition, no empty lines around the body.\nLayout/EmptyLinesAroundModuleBody:\n  Enabled: true\n\nLayout/FirstArgumentIndentation:\n  Enabled: true\n\n# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.\nStyle/HashSyntax:\n  Enabled: true\n\n# Method definitions after `private` or `protected` isolated calls need one\n# extra level of indentation.\nLayout/IndentationConsistency:\n  Enabled: true\n  EnforcedStyle: indented_internal_methods\n\n# Two spaces, no tabs (for indentation).\nLayout/IndentationWidth:\n  Enabled: true\n\nLayout/LeadingCommentSpace:\n  Enabled: true\n\nLayout/SpaceAfterColon:\n  Enabled: true\n\nLayout/SpaceAfterComma:\n  Enabled: true\n\nLayout/SpaceAfterSemicolon:\n  Enabled: true\n\nLayout/SpaceAroundEqualsInParameterDefault:\n  Enabled: true\n\nLayout/SpaceAroundKeyword:\n  Enabled: true\n\nLayout/SpaceAroundOperators:\n  Enabled: true\n\nLayout/SpaceBeforeComma:\n  Enabled: true\n\nLayout/SpaceBeforeFirstArg:\n  Enabled: true\n\nStyle/DefWithParentheses:\n  Enabled: true\n\n# Defining a method with parameters needs parentheses.\nStyle/MethodDefParentheses:\n  Enabled: true\n\nStyle/FrozenStringLiteralComment:\n  Enabled: true\n  EnforcedStyle: always\n\nStyle/RedundantFreeze:\n  Enabled: true\n\n# Use `foo {}` not `foo{}`.\nLayout/SpaceBeforeBlockBraces:\n  Enabled: true\n\n# Use `foo { bar }` not `foo {bar}`.\nLayout/SpaceInsideBlockBraces:\n  Enabled: true\n  EnforcedStyleForEmptyBraces: space\n\n# Use `{ a: 1 }` not `{a:1}`.\nLayout/SpaceInsideHashLiteralBraces:\n  Enabled: true\n\nLayout/SpaceInsideParens:\n  Enabled: true\n\n# Check quotes usage according to lint rule below.\nStyle/StringLiterals:\n  Enabled: true\n  EnforcedStyle: double_quotes\n\n# Detect hard tabs, no hard tabs.\nLayout/Tab:\n  Enabled: true\n\n# Blank lines should not have any spaces.\nLayout/TrailingEmptyLines:\n  Enabled: true\n\n# No trailing whitespace.\nLayout/TrailingWhitespace:\n  Enabled: true\n\n# Use quotes for string literals when they are enough.\nStyle/RedundantPercentQ:\n  Enabled: true\n\nLint/AmbiguousOperator:\n  Enabled: true\n\nLint/AmbiguousRegexpLiteral:\n  Enabled: true\n\nLint/ErbNewArguments:\n  Enabled: true\n\n# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.\nLint/RequireParentheses:\n  Enabled: true\n\nLint/ShadowingOuterLocalVariable:\n  Enabled: true\n\nLint/RedundantStringCoercion:\n  Enabled: true\n\nLint/UriEscapeUnescape:\n  Enabled: true\n\nLint/UselessAssignment:\n  Enabled: true\n\nLint/DeprecatedClassMethods:\n  Enabled: true\n\nStyle/ParenthesesAroundCondition:\n  Enabled: true\n\nStyle/RedundantBegin:\n  Enabled: true\n\nStyle/RedundantReturn:\n  Enabled: true\n  AllowMultipleReturnValues: true\n\nStyle/Semicolon:\n  Enabled: true\n  AllowAsExpressionSeparator: true\n\n# Prefer Foo.method over Foo::method\nStyle/ColonMethodCall:\n  Enabled: true\n\nStyle/TrivialAccessors:\n  Enabled: true\n\nPerformance/FlatMap:\n  Enabled: true\n\nPerformance/RedundantMerge:\n  Enabled: true\n\nPerformance/StartWith:\n  Enabled: true\n\nPerformance/EndWith:\n  Enabled: true\n\nPerformance/RegexpMatch:\n  Enabled: true\n\nPerformance/ReverseEach:\n  Enabled: true\n\nPerformance/UnfreezeString:\n  Enabled: true\n"
  },
  {
    "path": "FormSpec.md",
    "content": "## Why\n\nThe core of Petri Flow is the workflow engine. \nHowever, 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. \nSuch as selecting data from other data sources, data validation, application-oriented data fields, UI customization, etc. \nPetri 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).\n\n## Core Entity\n\n* Form\n* Field\n* Entry\n* FieldValue\n\n## Relations\n\n* form has_many fields\n* form has_many entries\n\n* field belongs_to form\n\n* field_value belongs_to form\n* field_value belongs_to field\n* field_value belongs_to entry\n\n* entry belongs_to form\n* entry belongs_to user\n* entry belongs_to workitem\n* entry has_many field_values\n\n## Casting\n\n* Field#cast(value)\n* FieldValue#value_after_cast\n\n## Setting\n\n```ruby\n# config/initializers/wf_config.rb\nWf.user_class   = \"::Wf::User\"\nWF.form_class   = \"::Form\"\nWf.entry_class  = \"::Entry\"\nWf.field_class  = \"::Field\"\n```\n"
  },
  {
    "path": "Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\n# Declare your gem's dependencies in wf.gemspec.\n# Bundler will treat runtime dependencies like base dependencies, and\n# development dependencies will be added by default to the :development group.\ngemspec\n\n# Declare any dependencies that are still in development here instead of in\n# your gemspec. These might include edge Rails or gems from your path or\n# Git. Remember to move these dependencies to your gemspec before releasing\n# your gem to rubygems.org.\n\n# To use a debugger\n# gem 'byebug', group: [:development, :test]\ngem \"annotate\"\ngem \"bootstrap\", \"~> 4.4.1\"\ngem \"bootstrap4-kaminari-views\"\ngem \"jquery-rails\"\ngem \"kaminari\"\ngem \"pg\"\ngem \"pry-rails\"\ngem \"simple_command\"\n\ngem \"loaf\"\ngem \"mysql2\"\ngem \"rubocop\"\ngem \"rubocop-performance\"\ngem \"rubocop-rails\"\ngem \"ruby-graphviz\", require: \"graphviz\"\n"
  },
  {
    "path": "Guard.md",
    "content": "## Guard Expression\n\nThere are two per-defined variables for your guard expression, `workitem`, `target`.\n\nSchema for `workitem`:\n\n```json\n{\n  \"id\":1,\n  \"case_id\":1,\n  \"workflow_id\":10,\n  \"transition_id\":24,\n  \"state\":\"enabled\",\n  \"enabled_at\":\"2020-02-24T12:37:28.459Z\",\n  \"started_at\":null,\n  \"canceled_at\":null,\n  \"finished_at\":null,\n  \"overridden_at\":null,\n  \"deadline\":null,\n  \"created_at\":\"2020-02-24T12:37:28.601Z\",\n  \"updated_at\":\"2020-02-24T12:37:28.601Z\",\n  \"trigger_time\":null,\n  \"holding_user_id\":null,\n  \"children_count\":3,\n  \"children_finished_count\":1,\n  \"forked\":false,\n  \"parent_id\":null,\n  \"holding_user\":{\n\n  },\n  \"form\":{\n\n  },\n  \"children\":[\n    {\n      \"id\":3,\n      \"case_id\":1,\n      \"workflow_id\":10,\n      \"transition_id\":24,\n      \"state\":\"enabled\",\n      \"enabled_at\":\"2020-02-24T12:37:28.459Z\",\n      \"started_at\":null,\n      \"canceled_at\":null,\n      \"finished_at\":null,\n      \"overridden_at\":null,\n      \"deadline\":null,\n      \"created_at\":\"2020-02-24T12:37:28.757Z\",\n      \"updated_at\":\"2020-02-24T12:37:28.757Z\",\n      \"trigger_time\":null,\n      \"holding_user_id\":\"7\",\n      \"children_count\":0,\n      \"children_finished_count\":0,\n      \"forked\":true,\n      \"parent_id\":1,\n      \"holding_user\":{\n        \"id\":7,\n        \"name\":\"User6\",\n        \"created_at\":\"2020-02-24T12:36:46.994Z\",\n        \"updated_at\":\"2020-02-24T12:36:46.994Z\",\n        \"group_id\":2\n      },\n      \"form\":{\n\n      },\n      \"children\":[\n\n      ]\n    },\n    {\n      \"id\":4,\n      \"case_id\":1,\n      \"workflow_id\":10,\n      \"transition_id\":24,\n      \"state\":\"enabled\",\n      \"enabled_at\":\"2020-02-24T12:37:28.459Z\",\n      \"started_at\":null,\n      \"canceled_at\":null,\n      \"finished_at\":null,\n      \"overridden_at\":null,\n      \"deadline\":null,\n      \"created_at\":\"2020-02-24T12:37:28.788Z\",\n      \"updated_at\":\"2020-02-24T12:37:28.788Z\",\n      \"trigger_time\":null,\n      \"holding_user_id\":\"5\",\n      \"children_count\":0,\n      \"children_finished_count\":0,\n      \"forked\":true,\n      \"parent_id\":1,\n      \"holding_user\":{\n        \"id\":5,\n        \"name\":\"User4\",\n        \"created_at\":\"2020-02-24T12:36:46.984Z\",\n        \"updated_at\":\"2020-02-24T12:36:46.984Z\",\n        \"group_id\":1\n      },\n      \"form\":{\n\n      },\n      \"children\":[\n\n      ]\n    },\n    {\n      \"id\":2,\n      \"case_id\":1,\n      \"workflow_id\":10,\n      \"transition_id\":24,\n      \"state\":\"finished\",\n      \"enabled_at\":\"2020-02-24T12:37:28.459Z\",\n      \"started_at\":null,\n      \"canceled_at\":null,\n      \"finished_at\":\"2020-02-24T12:37:46.221Z\",\n      \"overridden_at\":null,\n      \"deadline\":null,\n      \"created_at\":\"2020-02-24T12:37:28.700Z\",\n      \"updated_at\":\"2020-02-24T12:37:46.232Z\",\n      \"trigger_time\":null,\n      \"holding_user_id\":\"1\",\n      \"children_count\":0,\n      \"children_finished_count\":0,\n      \"forked\":true,\n      \"parent_id\":1,\n      \"holding_user\":{\n        \"id\":1,\n        \"name\":\"User0\",\n        \"created_at\":\"2020-02-24T12:36:46.963Z\",\n        \"updated_at\":\"2020-02-24T12:36:46.963Z\",\n        \"group_id\":4\n      },\n      \"form\":{\n        \"score\":90\n      },\n      \"children\":[\n\n      ]\n    }\n  ]\n}\n```"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Hooopo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright 2020 Hooopo Wang\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Petri Flow ![Ruby Gem](https://github.com/hooopo/petri_flow/workflows/Ruby%20Gem/badge.svg?event=push) ![Testing](https://github.com/hooopo/petri_flow/workflows/Testing/badge.svg?event=push)\n\nWorkflow engine for Rails.\n\n## Features\n* Full petri net features support (seq, parallel, iterative, timed, automitic etc.)\n* Both approval workflow and business workflow.\n* Simple web admin for workflow definition and case management.\n* Build-in simple dynamic form.\n* Replaceable dynamic form.\n* Support sub workflow.\n* Graph screen for workflow definition.\n* Graph screen for case and token migration.\n* Powerful guard expression.\n* MySQL and Postgres Support.\n* Powerful assignment management.\n* Flexible integration of organizational structure system(role, group, position or department etc.)\n\n## Docs\n\n* [Petri-Nets and Workflows](https://hooopo.gitbook.io/petri-flow/)\n* [Workflow Conceptual Guide](https://hooopo.gitbook.io/petri-flow/workflow-conceptual-guide)\n* [Workflow Concepts Reference](https://hooopo.gitbook.io/petri-flow/workflow-concepts-reference)\n* [Petri Flow ERD](https://hooopo.gitbook.io/petri-flow/erd)\n* [Developer Doc](https://hooopo.gitbook.io/petri-flow/developer-document)\n\n## Screenshots\n\n###  iterative routing\n\n![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-M-GhlU_QaD6nbLAbaJI%2F-M-X0nIxUUBwJsNhY4FN%2F-M-XAAQJbxDdaxoaYVda%2Fimage.png?alt=media&token=e74d1ae7-fa16-47ab-83b5-ad73a382fa07)\n\n### parallel_routing\n\n![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-M-GhlU_QaD6nbLAbaJI%2F-M-X0nIxUUBwJsNhY4FN%2F-M-XAKm9VN1MJxPZT9Xe%2Fimage.png?alt=media&token=c8beba84-72ec-470f-9987-81cf40762e15)\n\n### guard\n\n![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-M-GhlU_QaD6nbLAbaJI%2F-M-X0nIxUUBwJsNhY4FN%2F-M-XAT8Ui_xjqy9Niccp%2Fimage.png?alt=media&token=de4298fb-14b9-40bc-ab75-92ef0b98a533)\n\n### case state graph\n\n![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-M-GhlU_QaD6nbLAbaJI%2F-M-X0nIxUUBwJsNhY4FN%2F-M-XAeeR42ZRVIVKuUae%2Fimage.png?alt=media&token=90c96af9-d01f-4d6e-ae2b-445ea343a5ac)\n\n### \n## Installation\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'petri_flow', require: 'wf'\n```\n\nAnd then execute:\n\n```bash\n$ bundle\n```\n\nInstall graphviz\n\n```\nbrew install graphviz\n```\n\nMigration:\n\n```\nbundle exec rake wf:install:migrations\nbundle exec rails db:create\nbundle exec rails db:migrate\nbundle exec rails db:seed\n```\n## Usage\n\nAdd wf_config:\n\n```ruby\n# config/initializers/wf_config.rb\nWf.user_class = \"::User\"\nWf.org_classes = { group: \"::Group\" }\n```\n\nSet parties:\n\nFor normal org model, for example group or role etc.\n\n```ruby\nmodule Wf\n  class Group < ApplicationRecord\n    has_many :users \n    include Wf::ActsAsParty\n    acts_as_party(user: false, party_name: :name)\n  end\nend\n```\n\nFor user model:\n\n```ruby\nmodule Wf\n  class User < ApplicationRecord\n    belongs_to :group, optional: true\n    include Wf::ActsAsParty\n    acts_as_party(user: true, party_name: :name)\n  end\nend\n```\n\nthen\n\n```\nbundle exec rails \n```\n\nvisit:\n\n```\nhttp://localhost:3000/wf\n```\n\n## Testing\n\n* RAILS_ENV=test rake app:db:migrate && RAILS_ENV=test rake app:db:test:prepare && bundle exec rake test\n\n## Contributing\nContribution directions go here.\n\n## License\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n"
  },
  {
    "path": "Rakefile",
    "content": "# frozen_string_literal: true\n\nbegin\n  require \"bundler/setup\"\nrescue LoadError\n  puts \"You must `gem install bundler` and `bundle install` to run rake tasks\"\nend\n\nrequire \"rdoc/task\"\n\nRDoc::Task.new(:rdoc) do |rdoc|\n  rdoc.rdoc_dir = \"rdoc\"\n  rdoc.title    = \"Wf\"\n  rdoc.options << \"--line-numbers\"\n  rdoc.rdoc_files.include(\"README.md\")\n  rdoc.rdoc_files.include(\"lib/**/*.rb\")\nend\n\nAPP_RAKEFILE = File.expand_path(\"test/dummy/Rakefile\", __dir__)\nload \"rails/tasks/engine.rake\"\n\nload \"rails/tasks/statistics.rake\"\n\nrequire \"bundler/gem_tasks\"\n\nrequire \"rake/testtask\"\n\nRake::TestTask.new(:test) do |t|\n  t.libs << \"test\"\n  t.pattern = \"test/**/*_test.rb\"\n  t.verbose = false\nend\n\ntask default: :test\n"
  },
  {
    "path": "acts_as_party.md",
    "content": "## Usage\n\n\nfor normal org model, for example group or role etc.\n\n```ruby\nmodule Wf\n  class Group < ApplicationRecord\n    has_many :users \n    include Wf::ActsAsParty\n    acts_as_party(user: false, party_name: :name)\n  end\nend\n```\n\nfor user model:\n\n```ruby\nmodule Wf\n  class User < ApplicationRecord\n    belongs_to :group, optional: true\n    include Wf::ActsAsParty\n    acts_as_party(user: true, party_name: :name)\n  end\nend\n```"
  },
  {
    "path": "app/assets/config/wf_manifest.js",
    "content": "//= link_directory ../stylesheets/wf .css\n//= link_directory ../javascripts/wf .js\n"
  },
  {
    "path": "app/assets/images/wf/.keep",
    "content": ""
  },
  {
    "path": "app/assets/javascripts/wf/application.js",
    "content": "//= require jquery3\n//= require popper\n//= require bootstrap\n//= require rails-ujs\n//= require select2-full\n"
  },
  {
    "path": "app/assets/stylesheets/wf/application.scss",
    "content": "/*\n*= require select2\n*= require select2-bootstrap4\n*/\n\n@import \"uikit/index\";\n\nmain{\n  min-height: 80vh;\n  h2 {\n    margin-bottom: 1rem;\n  }\n}\n\n.footer{\n  text-align: center;\n  .container{\n    border-top: 1px solid rgb(245, 239, 228);\n    padding-top: 1rem;\n  }\n  a{\n    color: $gray-10;\n  }\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/arcs.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/cases.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/comments.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/fields.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/forms.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/guards.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/places.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/static_assignments.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/transitions.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/_colors.scss",
    "content": "// fork from: https://github.com/oortcast/42page/blob/master/app/javascript/42design/\n//\n// inspired by https://github.com/ant-design/ant-design\n//\n// The [colorPalette] means that the color is\n// generated by https://ant.design/docs/spec/colors#Palette-Generation-Tool\n\n// Brand Color\n$pagegreen-base: #00d192;\n$pagegreen-1: #f6fdfb; // tint background color\n$pagegreen-2: #cff5e8; // background color\n$pagegreen-3: #77f7c4; // [colorPalette]\n$pagegreen-4: #4bebb0; // [colorPalette]\n$pagegreen-5: #20CF97; // hover\n$pagegreen-6: $pagegreen-base; // normal\n$pagegreen-7: #00ab74; // click\n$pagegreen-8: #008566; // [colorPalette]\n$pagegreen-9: #005e4b; // [colorPalette]\n$pagegreen-10: #00382f; // [colorPalette]\n\n// Neutral Color\n$gray-1: #fff;\n$gray-2: #f7f7f7; //\n$gray-3: #f1f1f2; // table header\n$gray-4: #d2d3d4; // disable background\n$gray-5: #92989c; // border\n$gray-6: #868b8f; // disable text\n$gray-7: #7a7e81; // secondary text\n$gray-8: #53585c;\n$gray-9: #23292f; // primary text\n$gray-10: #000; // title\n\n// Color Palette\n\n$red-base: #fa4a4a;\n$red-1: #fff2f0; // [colorPalette]\n$red-2: #fff2f0;\n$red-3: #ffcdc7; // [colorPalette]\n$red-4: #ffa59e; // [colorPalette]\n$red-5: #ff7a75; // [colorPalette]\n$red-6: $red-base;\n$red-7: #d4353a; // [colorPalette]\n$red-8: #ad232c; // [colorPalette]\n$red-9: #871420; // [colorPalette]\n$red-10: #610e19; // [colorPalette]\n\n$yellow-base: #F9C84A;\n$yellow-1: #fffdf0; // [colorPalette]\n$yellow-2: #FFF3E0;\n$yellow-3: #fff5c7; // [colorPalette]\n$yellow-4: #ffea9e; // [colorPalette]\n$yellow-5: #ffdd75; // mark\n$yellow-6: $yellow-base;\n$yellow-7: #d4a135; // [colorPalette]\n$yellow-8: #ad7d23; // [colorPalette]\n$yellow-9: #875b14; // [colorPalette]\n$yellow-10: #613e0e; // [colorPalette]\n\n$blue-base: #3d90eb;\n$blue-1: #f0faff; // [colorPalette]\n$blue-2: #e6f5ff;\n$blue-3: #bde3ff; // [colorPalette]\n$blue-4: #94cfff; // [colorPalette]\n$blue-5: #68b2f7; // [colorPalette]\n$blue-6: $blue-base;\n$blue-7: #296fc4; // [colorPalette]\n$blue-8: #19519e; // [colorPalette]\n$blue-9: #0d3678; // [colorPalette]\n$blue-10: #082252; // [colorPalette]\n\n$cyan-base: #01C1B2;\n$cyan-1: #e6fff9; // [colorPalette]\n$cyan-2: #E3FFF8;\n$cyan-3: #72e8d2; // [colorPalette]\n$cyan-4: #48dbc5; // [colorPalette]\n$cyan-5: #23cfbb; // [colorPalette]\n$cyan-6: $cyan-base;\n$cyan-7: #009c94; // [colorPalette]\n$cyan-8: #007573; // [colorPalette]\n$cyan-9: #004e4f; // [colorPalette]\n$cyan-10: #002729; // [colorPalette]\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/_variables.scss",
    "content": "@import \"colors\";\n@import \"bootstrap/functions\";\n$blue:    $blue-base;\n$red:     $red-base;\n$yellow:  $yellow-base;\n$green:   $pagegreen-base;\n$cyan:    $cyan-base;\n$primary: $green;\n\n$gray-100: $gray-1;\n$gray-200: $gray-2;\n$gray-300: $gray-3; // table header\n$gray-400: $gray-4; // disable background\n$gray-500: $gray-5; // border\n$gray-600: $gray-6; // disable text\n$gray-700: $gray-7; // secondary text\n$gray-800: $gray-8;\n$gray-900: $gray-9; // primary text\n$secondary: $gray-7;\n\n$body-bg: #fffefd;\n\n$link-color:                              $primary;\n$link-hover-color:                        $pagegreen-5;\n\n$font-family-sans-serif: system, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\",\n\"Segoe UI Emoji\", \"Noto Color Emoji\", \"Segoe UI Symbol\";\n$font-family-monospace: \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, Courier, monospace;\n\n$line-height-base:            1.6;\n\n@import \"bootstrap/variables\";\n\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/alert.scss",
    "content": "@import \"variables\";\n\n.alert{\n  margin-bottom: 2rem;\n  p{\n    margin-bottom: 0;\n  }\n\n  &.alert-success{\n    background: $pagegreen-1;\n    border-color: $pagegreen-4;\n    color: $gray-9;\n  }\n  &.alert-warning{\n    background: $red-1;\n    border-color: $red-5;\n    color: $gray-9;\n  }\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/button.scss",
    "content": "@import \"_variables\";\n\n.btn-light{\n  color: $gray-8;\n  background: rgba(94, 94, 94, 0.2);\n  border: rgb(94,94,94);\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/card.scss",
    "content": ".card{\n  margin-bottom: 4rem;\n  border: 1px solid rgba(230, 230, 230, 0.41);\n  border-radius: 0.625rem;\n  box-shadow: 0 2px 6px 0 rgba(0,0,0,0.05);\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/index.scss",
    "content": "@import \"_variables\";\n@import \"bootstrap\";\n\n//overwrite\n@import \"navbar\";\n@import \"table\";\n@import \"card\";\n@import \"alert\";\n@import \"button\";\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/navbar.scss",
    "content": "@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: $white;\n  .navbar-brand a{\n    color: $gray-9;\n    &:hover{\n      text-decoration: none;\n      color: $gray-10;\n    }\n  }\n  .navbar-end a{\n    color: #7b7f82;\n    margin: 0 20px;\n    font-size: 18px;\n    border-bottom: transparent solid 2px;\n    &:hover{\n      color: $gray-9;\n      text-decoration: none;\n    }\n    &.is-active{\n      color: $gray-9;\n      border-bottom: $primary solid 2px;\n    }\n  }\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/uikit/table.scss",
    "content": "@import \"variables\";\n\n.table-view{\n  thead th{\n    color: $gray-7;\n    font-weight: 500;\n    border-top: 0;\n    border-bottom-width: 1px;\n    border-color: $gray-3;\n  }\n  tbody tr:hover {\n    background-color: $gray-2;\n  }\n  tbody td a:not(.btn) {\n    color: $gray-10;\n  }\n}\n"
  },
  {
    "path": "app/assets/stylesheets/wf/workflows.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/workitem_assignments.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/assets/stylesheets/wf/workitems.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "app/controllers/wf/application_controller.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class ApplicationController < ::ApplicationController\n    protect_from_forgery with: :exception\n    helper_method :wf_current_user\n\n    breadcrumb \"Home\", :root_path\n\n    def wf_current_user\n      current_user\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/arcs_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class ArcsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n\n    def new\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.new\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def create\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.new(arc_params)\n      if @arc.save\n        redirect_to workflow_path(@workflow), notice: \"arc was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def destroy\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.find(params[:id])\n      @arc.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def show\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def edit\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n      breadcrumb @arc.name, workflow_arc_path(@workflow, @arc)\n    end\n\n    def update\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @arc = @workflow.arcs.find(params[:id])\n      if @arc.update(arc_params)\n        redirect_to workflow_path(@workflow), notice: \"arc was successfully created.\"\n      else\n        render :edit\n      end\n    end\n\n    private\n\n      def arc_params\n        params.fetch(:arc, {}).permit(:direction, :transition_id, :place_id)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/cases_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class CasesController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n    def new\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @wf_case = @workflow.cases.new\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def create\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @wf_case = Wf::CaseCommand::New.call(@workflow, GlobalID::Locator.locate(case_params[:targetable])).result\n      Wf::CaseCommand::StartCase.call(@wf_case)\n      redirect_to workflow_cases_path(@workflow), notice: \"case created.\"\n    end\n\n    def index\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @cases = @workflow.cases.order(\"id DESC\")\n      @cases = @cases.where(state: params[:state].intern) if params[:state].present?\n      @cases = @cases.page(params[:page])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def show\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @wf_case = @workflow.cases.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def destroy\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @case = @workflow.cases.find(params[:id])\n      @case.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    private\n\n      def case_params\n        params.fetch(:case, {}).permit(:targetable, :target_id, :target_type)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/comments_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class CommentsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n\n    def new\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      @comment = @workitem.comments.new\n      breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)\n      breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)\n      breadcrumb @workitem.name, workitem_path(@workitem)\n    end\n\n    def create\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      Wf::CaseCommand::AddComment.call(@workitem, params[:comment][:body], wf_current_user)\n      redirect_to workitem_path(@workitem), notice: \"Comment Added.\"\n    end\n\n    def destroy\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      @comment = @workitem.comments.find(params[:id])\n      @comment.destroy\n      render js: \"window.location.reload()\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/fields_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class FieldsController < ApplicationController\n    breadcrumb \"Forms\", :forms_path\n\n    def new\n      @form = Wf::Form.find(params[:form_id])\n      @field = @form.fields.new\n      breadcrumb @form.name, form_path(@form)\n    end\n\n    def create\n      @form = Wf::Form.find(params[:form_id])\n      @field = @form.fields.new(field_params)\n      if @field.save\n        redirect_to form_path(@form), notice: \"field was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def destroy\n      @form = Wf::Form.find(params[:form_id])\n      @field = @form.fields.find(params[:id])\n      @field.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def edit\n      @form = Wf::Form.find(params[:form_id])\n      @field = @form.fields.find(params[:id])\n      breadcrumb @form.name, form_path(@form)\n    end\n\n    def update\n      @form = Wf::Form.find(params[:form_id])\n      @field = @form.fields.find(params[:id])\n      if @field.update(field_params)\n        redirect_to form_path(@form), notice: \"field was successfully created.\"\n      else\n        render :edit\n      end\n    end\n\n    private\n\n      def field_params\n        params.fetch(:field, {}).permit(:name, :form_id, :field_type, :position, :default_value)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/forms_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class FormsController < ApplicationController\n    breadcrumb \"Forms\", :forms_path\n\n    def index\n      @forms = Wf::Form.order(\"id DESC\").page(params[:page])\n    end\n\n    def new\n      @form = Wf::Form.new\n    end\n\n    def edit\n      @form = Wf::Form.find(params[:id])\n    end\n\n    def show\n      @form = Wf::Form.find(params[:id])\n    end\n\n    def destroy\n      @form = Wf::Form.find(params[:id])\n      @form.destroy\n      respond_to do |format|\n        format.html { redirect_to forms_path, notice: \"form was successfully deleted.\" }\n        format.js { render js: \"window.location.reload();\" }\n      end\n    end\n\n    def update\n      @form = Wf::Form.find(params[:id])\n      if @form.update(form_params)\n        redirect_to form_path(@form), notice: \"form was successfully updated.\"\n      else\n        render :edit\n      end\n    end\n\n    def create\n      @form = Wf::Form.new(form_params)\n\n      if @form.save\n        redirect_to forms_path, notice: \"form was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    private\n\n      def form_params\n        params.fetch(:form, {}).permit(:name, :description)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/guards_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class GuardsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n    def new\n      @arc = Wf::Arc.find(params[:arc_id])\n      @guard = @arc.guards.new\n      breadcrumb @arc.workflow.name, workflow_path(@arc.workflow)\n      breadcrumb @arc.name, workflow_arc_path(@arc.workflow, @arc)\n    end\n\n    def create\n      @arc = Wf::Arc.find(params[:arc_id])\n      gp = guard_params.merge(fieldable: GlobalID::Locator.locate(guard_params[:fieldable]))\n      @guard = @arc.guards.new(gp.merge(workflow: @arc.workflow))\n      redirect_to workflow_arc_path(@arc.workflow, @arc), notice: \"only out direction arc can set guard!\" unless @arc.out?\n      if @guard.save\n        redirect_to workflow_arc_path(@arc.workflow, @arc), notice: \"guard was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def destroy\n      @arc = Wf::Arc.find(params[:arc_id])\n      @guard = @arc.guards.find(params[:id])\n      @guard.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def edit\n      @arc = Wf::Arc.find(params[:arc_id])\n      @guard = @arc.guards.find(params[:id])\n      breadcrumb @arc.workflow.name, workflow_path(@arc.workflow)\n      breadcrumb @arc.name, workflow_arc_path(@arc.workflow, @arc)\n    end\n\n    def update\n      @arc = Wf::Arc.find(params[:arc_id])\n      gp = guard_params.merge(fieldable: GlobalID::Locator.locate(guard_params[:fieldable]))\n      @guard = @arc.guards.find(params[:id])\n      if @guard.update(gp)\n        redirect_to workflow_arc_path(@arc.workflow, @arc), notice: \"guard was successfully created.\"\n      else\n        render :edit\n      end\n    end\n\n    private\n\n      def guard_params\n        params.fetch(:guard, {}).permit(:fieldable, :fieldable_type, :fieldable_id, :op, :value, :exp)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/places_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class PlacesController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n    def new\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @place = @workflow.places.new\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def create\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @place = @workflow.places.new(place_params)\n      if @place.save\n        redirect_to workflow_path(@workflow), notice: \"place was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def destroy\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @place = @workflow.places.find(params[:id])\n      @place.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def edit\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @place = @workflow.places.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def update\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @place = @workflow.places.find(params[:id])\n      if @place.update(place_params)\n        redirect_to workflow_path(@workflow), notice: \"place was successfully created.\"\n      else\n        render :edit\n      end\n    end\n\n    private\n\n      def place_params\n        params.fetch(:place, {}).permit(:name, :description, :place_type, :sort_order)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/static_assignments_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class StaticAssignmentsController < ApplicationController\n    def new\n      @transition = Wf::Transition.find(params[:transition_id])\n      @static_assignment = @transition.transition_static_assignments.new\n    end\n\n    def create\n      @transition = Wf::Transition.find(params[:transition_id])\n      @party = Wf::Party.find(permit_params[:party_id])\n      @static_assignment = @transition.transition_static_assignments.new(party: @party)\n      if @static_assignment.save\n        redirect_to workflow_transition_path(@transition.workflow, @transition), notice: \"static assignment was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def destroy\n      @transition = Wf::Transition.find(params[:transition_id])\n      @static_assignment = @transition.transition_static_assignments.find(params[:id])\n      @static_assignment.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def permit_params\n      params.fetch(:transition_static_assignment, {}).permit(:party_id)\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/transitions_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class TransitionsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n\n    def new\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @transition = @workflow.transitions.new\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def show\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @transition = @workflow.transitions.find(params[:id])\n    end\n\n    def create\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      tp = transition_params.merge(form: GlobalID::Locator.locate(transition_params[:form]))\n      @transition = @workflow.transitions.new(tp)\n      if @transition.save\n        redirect_to workflow_path(@workflow), notice: \"transition was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    def edit\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @transition = @workflow.transitions.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def destroy\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @transition = @workflow.transitions.find(params[:id])\n      @transition.destroy\n      render js: \"window.location.reload()\"\n    end\n\n    def update\n      @workflow = Wf::Workflow.find(params[:workflow_id])\n      @transition = @workflow.transitions.find(params[:id])\n      tp = transition_params.merge(form: GlobalID::Locator.locate(transition_params[:form]))\n      if @transition.update(tp)\n        redirect_to workflow_path(@workflow), notice: \"transition was successfully updated.\"\n      else\n        render :edit\n      end\n    end\n\n    private\n\n      def transition_params\n        params.fetch(:transition, {}).permit(\n          :name,\n          :description,\n          :trigger_limit,\n          :trigger_type,\n          :sort_order,\n          :form,\n          :enable_callback,\n          :fire_callback,\n          :time_callback,\n          :hold_timeout_callback,\n          :assignment_callback,\n          :unassignment_callback,\n          :notification_callback,\n          :deadline_callback,\n          :sub_workflow_id,\n          :multiple_instance,\n          :finish_condition,\n          :dynamic_assign_by_id\n        )\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/workflows_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class WorkflowsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n    def index\n      @workflows = Wf::Workflow.order(\"id DESC\").page(params[:page])\n    end\n\n    def new\n      @workflow = Wf::Workflow.new\n    end\n\n    def edit\n      @workflow = Wf::Workflow.find(params[:id])\n      breadcrumb @workflow.name, workflow_path(@workflow)\n    end\n\n    def show\n      @workflow = Wf::Workflow.find(params[:id])\n    end\n\n    def destroy\n      @workflow = Wf::Workflow.find(params[:id])\n      @workflow.destroy\n      respond_to do |format|\n        format.html { redirect_to workflows_path, notice: \"workflow was successfully deleted.\" }\n        format.js { render js: \"window.location.reload();\" }\n      end\n    end\n\n    def update\n      @workflow = Wf::Workflow.find(params[:id])\n      if @workflow.update(workflow_params)\n        redirect_to workflow_path(@workflow), notice: \"workflow was successfully updated.\"\n      else\n        render :edit\n      end\n    end\n\n    def create\n      @workflow = Wf::Workflow.new(workflow_params)\n\n      if @workflow.save\n        redirect_to workflows_path, notice: \"workflow was successfully created.\"\n      else\n        render :new\n      end\n    end\n\n    private\n\n      def workflow_params\n        params.fetch(:workflow, {}).permit(:name, :description)\n      end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/workitem_assignments_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class WorkitemAssignmentsController < ApplicationController\n    breadcrumb \"Workflows\", :workflows_path\n    def new\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      @workitem_assignment = @workitem.workitem_assignments.new(party_id: params[:party_id])\n      breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)\n      breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)\n      breadcrumb @workitem.name, workitem_path(@workitem)\n    end\n\n    def create\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      party = Wf::Party.find(params[:workitem_assignment][:party_id])\n      Wf::CaseCommand::AddWorkitemAssignment.call(@workitem, party)\n      redirect_to workitem_path(@workitem), notice: \"assigned party to workitem.\"\n    end\n\n    def destroy\n      @workitem = Wf::Workitem.find(params[:workitem_id])\n      party = Wf::Party.find(params[:party_id])\n      Wf::CaseCommand::RemoveWorkitemAssignment.call(@workitem, party)\n      render js: \"window.location.reload()\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/controllers/wf/workitems_controller.rb",
    "content": "# frozen_string_literal: true\n\nrequire_dependency \"wf/application_controller\"\n\nmodule Wf\n  class WorkitemsController < ApplicationController\n    before_action :find_workitem, except: [:index]\n    before_action :check_start, only: [:start]\n    before_action :check_finish, only: %i[pre_finish finish]\n\n    breadcrumb \"Workflows\", :workflows_path\n\n    def index\n      @workitems = Wf::Workitem.todo(wf_current_user)\n      @workitems = @workitems.where(state: params[:state].intern) if params[:state]\n      @workitems = @workitems.where(state: params[:state].intern) if params[:state].present?\n      @workitems = @workitems.distinct.order(\"id desc\").page(params[:page])\n    end\n\n    def show\n      breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)\n      breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)\n    end\n\n    def start\n      Wf::CaseCommand::StartWorkitem.call(@workitem, wf_current_user)\n      breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)\n      breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)\n      breadcrumb @workitem.name, workitem_path(@workitem)\n      render :pre_finish\n    end\n\n    def pre_finish\n      breadcrumb @workitem.workflow.name, workflow_path(@workitem.workflow)\n      breadcrumb @workitem.case.name, workflow_case_path(@workitem.workflow, @workitem.case)\n      breadcrumb @workitem.name, workitem_path(@workitem)\n    end\n\n    def finish\n      if dynamic_assignments = params.dig(:workitem, :dynamic_assignments)\n        dynamic_assignments.permit!.each do |t_id, party_id|\n          Wf::CaseCommand::AddManualAssignment.call(@workitem.case, @workitem.workflow.transitions.find(t_id), Wf::Party.find(party_id))\n        end\n      end\n      if @workitem.transition.form && params[:workitem][:entry]\n        form = @workitem.transition.form\n        cmd = Wf::CaseCommand::CreateEntry.call(form, @workitem, wf_current_user, params[:workitem][:entry].permit!)\n        if cmd.success?\n          Wf::CaseCommand::FinishWorkitem.call(@workitem)\n          finish_and_redirect\n        else\n          redirect_to pre_finish_workitem_path(@workitem), notice: \"Your input no OK.\"\n        end\n      else\n        Wf::CaseCommand::FinishWorkitem.call(@workitem)\n        finish_and_redirect\n      end\n    end\n\n    def finish_and_redirect\n      if @workitem.case.finished?\n        if started_by = @workitem.case.started_by_workitem\n          redirect_to workflow_case_path(started_by.workflow, started_by.case), notice: \"workitem is done, and goto parent case.\"\n        else\n          redirect_to workflow_case_path(@workitem.workflow, @workitem.case), notice: \"workitem is done, and the case is finished.\"\n        end\n      else\n        redirect_to workitem_path(@workitem.case.workitems.enabled.first), notice: \"workitem is done, and goto next fireable workitem.\"\n      end\n    end\n\n    def find_workitem\n      @workitem = Wf::Workitem.find(params[:id])\n    end\n\n    def check_start\n      unless @workitem.started_by?(wf_current_user)\n        redirect_to workitem_path(@workitem), notice: \"You can not start this workitem, Please assign to youself first.\"\n      end\n    end\n\n    def check_finish\n      unless @workitem.finished_by?(wf_current_user)\n        redirect_to workitem_path(@workitem), notice: \"You can not the holding use of this workitem, Please assign to youself && start it first.\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/application_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module ApplicationHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/arcs_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module ArcsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/cases_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module CasesHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/comments_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module CommentsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/fields_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module FieldsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/forms_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module FormsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/guards_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module GuardsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/places_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module PlacesHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/static_assignments_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module StaticAssignmentsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/transitions_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module TransitionsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/workflows_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module WorkflowsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/workitem_assignments_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module WorkitemAssignmentsHelper\n  end\nend\n"
  },
  {
    "path": "app/helpers/wf/workitems_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module WorkitemsHelper\n  end\nend\n"
  },
  {
    "path": "app/jobs/wf/application_job.rb",
    "content": "# 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",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class FireTimedWorkitemJob < ApplicationJob\n    queue_as :default\n\n    def perform(workitem_id)\n      item = Wf::Workitem.find(workitem_id)\n      if item.trigger_time && item.enabled? && item.case.active?\n        CaseCommand::FireTransitionInternal.call(item)\n        CaseCommand::SweepAutomaticTransitions.call(item.case)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/mailers/wf/application_mailer.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class ApplicationMailer < ActionMailer::Base\n    default from: \"from@example.com\"\n    layout \"mailer\"\n  end\nend\n"
  },
  {
    "path": "app/models/wf/acts_as_party.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"active_support/concern\"\n\nmodule Wf\n  module ActsAsParty\n    extend ActiveSupport::Concern\n\n    included do\n      has_one :party, as: :partable\n    end\n\n    module ClassMethods\n      def acts_as_party(options = { user: false, party_name: :name })\n        cattr_accessor :yaffle_text_field\n        has_many :users, foreign_key: :id if options[:user]\n        after_create do\n          create_party(party_name: options[:party_name])\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/application_record.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class ApplicationRecord < ActiveRecord::Base\n    self.abstract_class = true\n  end\nend\n"
  },
  {
    "path": "app/models/wf/arc.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_arcs\n#\n#  id            :integer          not null, primary key\n#  workflow_id   :integer\n#  transition_id :integer\n#  place_id      :integer\n#  direction     :integer          default(\"0\")\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#  guards_count  :integer          default(\"0\")\n#\n\nmodule Wf\n  class Arc < ApplicationRecord\n    belongs_to :workflow, touch: true\n    belongs_to :transition\n    belongs_to :place\n\n    has_many :guards, dependent: :destroy\n\n    scope :with_guards, -> { where(\"guards_count > 0\") }\n    scope :without_guards, -> { where(guards_count: 0) }\n\n    # direction is relative to the transition\n    enum direction: {\n      in: 0,\n      out: 1\n    }\n\n    def name\n      if in?\n        [place&.name, transition&.name].join(\" -> \")\n      else\n        [transition&.name, place&.name].join(\" -> \")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/assignment_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class AssignmentDefault < ApplicationJob\n    queue_as :default\n\n    def perform(_workitem_id)\n      # return Party array.\n      []\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/deadline_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class DeadlineDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/enable_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class EnableDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/fire_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class FireDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/hold_timeout_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class HoldTimeoutDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/notification_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class NotificationDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/time_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class TimeDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/callbacks/unassignment_default.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::Callbacks\n  class UnassignmentDefault < ApplicationJob\n    queue_as :default\n\n    def perform(*guests)\n      $stdout.puts(guests.inspect)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_cases\n#\n#  id                     :integer          not null, primary key\n#  workflow_id            :integer\n#  targetable_type        :string\n#  targetable_id          :string\n#  state                  :integer          default(\"0\")\n#  created_at             :datetime         not null\n#  updated_at             :datetime         not null\n#  started_by_workitem_id :integer\n#\n\nmodule Wf\n  class Case < ApplicationRecord\n    belongs_to :workflow\n    belongs_to :targetable, optional: true, polymorphic: true\n    belongs_to :started_by_workitem, optional: true, class_name: \"Wf::Workitem\"\n    has_many :workitems\n    has_many :tokens\n    has_many :case_assignments\n    has_many :parties, through: :case_assignments, source: \"party\"\n\n    enum state: {\n      created: 0,\n      active: 1,\n      suspended: 2,\n      canceled: 3,\n      finished: 4\n    }\n\n    def can_fire?(transition)\n      ins = transition.arcs.in.to_a\n      return false if ins.blank?\n\n      ins.all? { |arc| arc.place.tokens.where(case: self).where(state: :free).exists? }\n    end\n\n    def name\n      \"Case->#{id}\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_assignment.rb",
    "content": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_case_assignments\n#\n#  id            :integer          not null, primary key\n#  case_id       :integer\n#  transition_id :integer\n#  party_id      :integer\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\n# frozen_string_literal: true\n\nmodule Wf\n  class CaseAssignment < ApplicationRecord\n    belongs_to :case\n    belongs_to :transition\n    belongs_to :party\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/add_comment.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class AddComment\n    prepend SimpleCommand\n    attr_reader :workitem, :comment, :user\n    def initialize(workitem, comment, user)\n      @workitem   = workitem\n      @comment    = comment\n      @user       = user\n    end\n\n    def call\n      workitem.comments.create!(user: user, body: comment)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/add_manual_assignment.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class AddManualAssignment\n    prepend SimpleCommand\n    attr_reader :wf_case, :transition, :party\n    def initialize(wf_case, transition, party)\n      @wf_case = wf_case\n      @transition = transition\n      @party = party\n    end\n\n    def call\n      wf_case.case_assignments.find_or_create_by!(transition: transition, party: party)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/add_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class AddToken\n    prepend SimpleCommand\n    attr_reader :wf_case, :place\n    def initialize(wf_case, place)\n      @wf_case = wf_case\n      @place = place\n    end\n\n    def call\n      wf_case.tokens.create!(\n        workflow: wf_case.workflow,\n        place: place,\n        state: :free\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/add_workitem_assignment.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class AddWorkitemAssignment\n    prepend SimpleCommand\n    attr_reader :workitem, :party, :permanent\n    def initialize(workitem, party, permanent = true)\n      @workitem = workitem\n      @party    = party\n      @permanent = permanent\n    end\n\n    def call\n      return if party.nil?\n\n      Wf::ApplicationRecord.transaction do\n        AddManualAssignment.call(workitem.case, workitem.transition, party) if permanent\n\n        notified_users = workitem.parties.map do |p|\n          p.partable.users.to_a\n        end.flatten\n\n        assign = workitem.workitem_assignments.where(party: party).first\n        break if assign\n\n        workitem.workitem_assignments.create!(party: party)\n        new_users = party.partable.users.to_a\n        to_notify = new_users - notified_users\n        transition = workitem.transition\n        to_notify.each do |user|\n          # TODO: multiple instance + sub workflow\n          if transition.multiple_instance? && !workitem.forked?\n            next if workitem.children.where(holding_user: user).exists?\n\n            child = workitem.children.create!(\n              workflow_id: workitem.workflow_id,\n              transition_id: workitem.transition_id,\n              state: :enabled,\n              trigger_time: workitem.trigger_time,\n              forked: true,\n              holding_user: user,\n              case_id: workitem.case_id\n            )\n            workitem.transition.notification_callback.constantize.new(child, user.id).perform_now\n          else\n            workitem.transition.notification_callback.constantize.new(workitem, user.id).perform_now\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/begin_workitem_action.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class BeginWorkitemAction\n    prepend SimpleCommand\n    attr_reader :workitem, :action, :user\n    def initialize(workitem, user, action = :start)\n      @workitem = workitem\n      @action   = action\n      @user     = user\n    end\n\n    def call\n      if action == :start\n        raise(\"Workitem is in state #{workitem.state}, but it must be in state enabled to be started.\") unless workitem.enabled?\n        raise(\"You are not assigned to this workitem.\") unless workitem.owned_by?(user)\n      elsif action == :finish || action == :cancel\n        if workitem.started?\n          raise(\"You are not the user currently working on this workitem.\") if workitem.holding_user != user\n        elsif workitem.enabled?\n          raise(\"You can only cancel a workitem in state started, but this workitem is in state #{workitem.state}.\") if action == :cancel\n          raise(\"You are not assigned to this workitem.\") unless workitem.owned_by?(user)\n\n          workitem.update!(holding_user: user)\n        else\n          raise(\"Workitem is in state #{workitem.state}, but it must be in state enabled or started to be finished.\")\n        end\n      elsif action == :comment\n        # TODO\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/cancel.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class Cancel\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      raise(\"Only active or suspended cases can be canceled\") unless wf_case.suspended? || wf_case.active?\n\n      wf_case.canceled!\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/cancel_workitem.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class CancelWorkitem\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      raise(\"The workitem is not in state #{workitem.state}\") unless workitem.started?\n\n      Wf::ApplicationRecord.transaction do\n        workitem.update!(state: :canceled, canceled_at: Time.zone.now)\n        ReleaseToken.call(workitem)\n        SweepAutomaticTransitions.call(workitem.case)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/clear_manual_assignments.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class ClearManualAssignments\n    prepend SimpleCommand\n    attr_reader :wf_case, :transition\n    def initialize(wf_case, transition)\n      @wf_case = wf_case\n      @transition = transition\n    end\n\n    def call\n      wf_case.case_assignments.where(transition: transition).find_each(&:destroy)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/clear_workitem_assignments.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class ClearWorkitemAssignments\n    prepend SimpleCommand\n    attr_reader :workitem, :permanent\n    def initialize(workitem, permanent = true)\n      @workitem = workitem\n      @permanent = permanent\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        ClearManualAssignments.call(workitem.case, workitem.transition) if permanent\n        workitem.workitem_assignments.delete_all\n        workitem.transition.unassignment_callback.constantize.new(workitem.id).perform\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/consume_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class ConsumeToken\n    prepend SimpleCommand\n    attr_reader :wf_case, :place, :locked_item\n    def initialize(wf_case, place, locked_item = nil)\n      @wf_case  = wf_case\n      @place    = place\n      @locked_item = locked_item\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        if locked_item\n          wf_case.tokens.where(place: place, state: :locked, locked_workitem_id: locked_item.id).update(consumed_at: Time.zone.now, state: :consumed)\n        else\n          wf_case.tokens.where(id: wf_case.tokens.where(place: place, state: :free).first&.id).update(consumed_at: Time.zone.now, state: :consumed)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/create_entry.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class CreateEntry\n    prepend SimpleCommand\n    attr_reader :form, :workitem, :user, :params\n    def initialize(form, workitem, user, params)\n      @form       = form\n      @workitem   = workitem\n      @params     = params\n      @user       = user\n    end\n\n    def call\n      create_entry\n    rescue StandardError\n      binding.pry\n      puts $ERROR_INFO\n      # TODO: more detail\n      errors.add(:base, :failure)\n    end\n\n    def create_entry\n      Wf::ApplicationRecord.transaction do\n        entry = form.entries.find_or_create_by!(user: user, workitem: workitem)\n        params.each do |field_id, field_value|\n          if field = entry.field_values.where(form: form, field_id: field_id).first\n            field.update!(value: field_value)\n          else\n            entry.field_values.create!(form: form, field_id: field_id, value: field_value)\n          end\n        end\n        entry.update_payload!\n        entry\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/enable_transitions.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class EnableTransitions\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        wf_case.workitems.enabled.each do |workitem|\n          workitem.update!(state: :overridden, overridden_at: Time.zone.now) unless wf_case.can_fire?(workitem.transition)\n        end\n        wf_case.workflow.transitions.each do |transition|\n          next unless wf_case.can_fire?(transition) && !transition.workitems.where(case: wf_case, state: %i[enabled started]).exists?\n\n          trigger_time = Time.zone.now + transition.trigger_limit.minutes if transition.trigger_limit && transition.time?\n          workitem = wf_case.workitems.create!(\n            workflow: wf_case.workflow,\n            transition: transition,\n            state: :enabled,\n            trigger_time: trigger_time\n          )\n          Wf::FireTimedWorkitemJob.set(wait: transition.trigger_limit.minutes).perform_later(workitem.id) if trigger_time\n          SetWorkitemAssignments.call(workitem)\n          workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0\n          if sub_workflow = transition.sub_workflow\n            sub_case = Wf::CaseCommand::New.call(sub_workflow, nil, workitem).result\n            Wf::CaseCommand::StartCase.call(sub_case)\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/end_workitem_action.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class EndWorkitemAction\n    prepend SimpleCommand\n    attr_reader :workitem, :action, :user\n    def initialize(workitem, user, action = :start)\n      @workitem = workitem\n      @action   = action\n      @user     = user\n    end\n\n    def call\n      if action == :start\n        StartWorkitem.call(workitem, user)\n      elsif action == :finish\n        FinishWorkitem.call(workitem, user)\n      elsif action == :cancel\n        CancelWorkitem.call(workitem, user)\n      elsif action == :comment\n        raise(\"Unknown action #{action}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/finish_workitem.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class FinishWorkitem\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        if workitem.forked?\n          workitem.update!(finished_at: Time.zone.now, state: :finished)\n          Wf::Workitem.increment_counter(:children_finished_count, workitem.parent_id)\n          if parent = workitem.parent\n            if (parent.children_finished_count >= parent.children_count) || workitem.transition.finish_condition.constantize.new.perform(workitem)\n              parent.children.where(state: %i[started enabled]).find_each do |wi|\n                wi.update!(overridden_at: Time.zone.now, state: :overridden)\n              end\n              FireTransitionInternal.call(parent)\n              SweepAutomaticTransitions.call(parent.case)\n            end\n          end\n        else\n          FireTransitionInternal.call(workitem)\n          SweepAutomaticTransitions.call(workitem.case)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/finished_p.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class FinishedP\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      return true if wf_case.finished?\n\n      end_place = wf_case.workflow.places.end.first\n      end_place_token_num = Wf::ApplicationRecord.uncached { wf_case.tokens.where(place: end_place).count }\n      if end_place_token_num == 0\n        false\n      else\n        free_and_locked_token_num = wf_case.tokens.where(place: end_place).where(state: %i[free locked]).count\n        raise(\"The workflow net is misconstructed: Some parallel executions have not finished.\") if free_and_locked_token_num > 1\n\n        ConsumeToken.call(wf_case, end_place)\n        unless wf_case.finished?\n          wf_case.finished!\n          if started_by_workitem = wf_case.started_by_workitem\n            Wf::CaseCommand::FinishWorkitem.call(started_by_workitem)\n          end\n        end\n        true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/fire_message_transition.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class FireMessageTransition\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      raise(\"Transition #{workitem.transition.name} is not message triggered\") unless workitem.transition.message?\n\n      Wf::ApplicationRecord.transaction do\n        FireTransitionInternal.call(workitem)\n        SweepAutomaticTransitions.call(workitem.case)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/fire_transition_internal.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class FireTransitionInternal\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      if workitem.enabled?\n        locked_item = nil\n      elsif workitem.started?\n        locked_item = workitem\n      else\n        raise(\"can not fire the transition if it is not in state enabled or started.\")\n      end\n      Wf::ApplicationRecord.transaction do\n        workitem.update!(finished_at: Time.zone.now, state: :finished)\n        # TODO: only in?\n        workitem.transition.arcs.each do |arc|\n          ConsumeToken.call(workitem.case, arc.place, locked_item)\n        end\n        # last arc without guard -> pass\n        has_passed = false\n        workitem.transition.arcs.out.order(\"guards_count DESC\").each do |arc|\n          if workitem.transition.explicit_or_split?\n            if workitem.pass_guard?(arc, has_passed)\n              has_passed = true\n              AddToken.call(workitem.case, arc.place)\n            end\n          else\n            AddToken.call(workitem.case, arc.place)\n          end\n        end\n        workitem.transition.fire_callback.constantize.new(workitem.id).perform_now\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/lock_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class LockToken\n    prepend SimpleCommand\n    attr_reader :wf_case, :place, :workitem\n    def initialize(wf_case, place, workitem)\n      @wf_case = wf_case\n      @place = place\n      @workitem = workitem\n    end\n\n    def call\n      wf_case.tokens.free.where(place: place).limit(1).update_all(\n        state: :locked,\n        locked_at: Time.zone.now,\n        locked_workitem_id: workitem.id\n      )\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/new.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class New\n    prepend SimpleCommand\n    attr_reader :workflow, :target, :started_by\n    def initialize(workflow, target = nil, started_by = nil)\n      @workflow = workflow\n      @target = target\n      @started_by = started_by\n    end\n\n    def call\n      wf_case = workflow.cases.create!(targetable: target, started_by_workitem: started_by, state: :created)\n      wf_case\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/release_token.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class ReleaseToken\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        Wf::Token.where(locked_workitem_id: workitem.id).locked.each do |token|\n          AddToken.call(token.case, token.place)\n          token.update!(state: :canceled, canceled_at: Time.zone.now)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/remove_manual_assignment.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class RemoveManualAssignment\n    prepend SimpleCommand\n    attr_reader :wf_case, :transition, :party\n    def initialize(wf_case, transition, party)\n      @wf_case = wf_case\n      @transition = transition\n      @party = party\n    end\n\n    def call\n      wf_case.case_assignments.where(transition: transition, party: party).find_each(&:destroy)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/remove_workitem_assignment.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class RemoveWorkitemAssignment\n    prepend SimpleCommand\n    attr_reader :workitem, :party, :permanent\n    def initialize(workitem, party, permanent = true)\n      @workitem = workitem\n      @party    = party\n      @permanent = permanent\n    end\n\n    def call\n      return if party.nil?\n\n      Wf::ApplicationRecord.transaction do\n        RemoveManualAssignment.call(workitem.case, workitem.transition, party) if permanent\n        workitem.workitem_assignments.where(party: party).first&.destroy\n\n        workitem.transition.unassignment_callback.constantize.new(workitem.id).perform_now if workitem.workitem_assignments.count == 0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/resume.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class Resume\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      raise(\"Only suspended or canceled cases can be resumed\") unless wf_case.suspended? || wf_case.canceled?\n\n      wf_case.active!\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/set_workitem_assignments.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class SetWorkitemAssignments\n    prepend SimpleCommand\n    attr_reader :workitem\n    def initialize(workitem)\n      @workitem = workitem\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        has_case_ass = false\n        workitem.case.case_assignments.where(transition: workitem.transition).find_each do |case_ass|\n          AddWorkitemAssignment.call(workitem, case_ass.party, false)\n          has_case_ass = true\n        end\n\n        unless has_case_ass\n          callback_parties = workitem.transition.assignment_callback.constantize.new.perform(workitem.id)\n          if callback_parties.present?\n            callback_parties.each do |party|\n              AddWorkitemAssignment.call(workitem, party, false)\n            end\n          else\n            workitem.transition.transition_static_assignments.each do |static_assignment|\n              AddWorkitemAssignment.call(workitem, static_assignment.party, false)\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/start_case.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class StartCase\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        wf_case.active!\n        AddToken.call(wf_case, wf_case.workflow.places.start.first)\n        SweepAutomaticTransitions.call(wf_case)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/start_workitem.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class StartWorkitem\n    prepend SimpleCommand\n    attr_reader :workitem, :user\n    def initialize(workitem, user)\n      @workitem = workitem\n      @user     = user\n    end\n\n    def call\n      raise(\"The workitem can not run by user.\") unless workitem.real?\n      raise(\"The workitem is not in state #{workitem.state}\") unless workitem.enabled?\n\n      # TODO: holding timeout\n      Wf::ApplicationRecord.transaction do\n        workitem.update!(state: :started, holding_user: user)\n\n        workitem.transition.arcs.in.each do |arc|\n          LockToken.call(workitem.case, arc.place, workitem)\n        end\n      end\n      workitem\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/suspend.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class Suspend\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      raise(\"Only active or suspended cases can be canceled\") unless wf_case.active?\n\n      wf_case.suspended!\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/sweep_automatic_transitions.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class SweepAutomaticTransitions\n    prepend SimpleCommand\n    attr_reader :wf_case\n    def initialize(wf_case)\n      @wf_case = wf_case\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        EnableTransitions.call(wf_case)\n        done = false\n        until done\n          done = true\n          finished = FinishedP.call(wf_case).result\n          next if finished\n\n          Wf::ApplicationRecord.uncached do\n            wf_case.workitems.joins(:transition).where(state: :enabled).where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:automatic] }).find_each do |item|\n              FireTransitionInternal.call(item)\n              done = false\n            end\n          end\n          EnableTransitions.call(wf_case)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/sweep_timed_transitions.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class SweepTimedTransitions\n    prepend SimpleCommand\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        Wf::Workitem.enabled.where(\"trigger_time <= ?\", Time.zone.now).find_each do |item|\n          FireTransitionInternal.call(item)\n          SweepAutomaticTransitions.call(item.case)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/case_command/workitem_action.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf::CaseCommand\n  class WorkitemAction\n    prepend SimpleCommand\n    attr_reader :workitem, :action, :user\n    def initialize(workitem, user, action = :start)\n      @workitem = workitem\n      @action   = action\n      @user     = user\n    end\n\n    def call\n      Wf::ApplicationRecord.transaction do\n        BeginWorkitemAction.call(workitem, user, action)\n        EndWorkitemAction.call(workitem, user, action)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/comment.rb",
    "content": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_comments\n#\n#  id          :integer          not null, primary key\n#  workitem_id :integer\n#  user_id     :string\n#  body        :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\n# frozen_string_literal: true\n\nmodule Wf\n  class Comment < ApplicationRecord\n    belongs_to :workitem\n    belongs_to :user, class_name: Wf.user_class.to_s\n  end\nend\n"
  },
  {
    "path": "app/models/wf/demo_target.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_demo_targets\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :string\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nmodule Wf\n  class DemoTarget < ApplicationRecord\n    has_many :cases, as: :targetable\n  end\nend\n"
  },
  {
    "path": "app/models/wf/entry.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_entries\n#\n#  id          :integer          not null, primary key\n#  user_id     :string\n#  workitem_id :integer\n#  payload     :json             default(\"{}\")\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#  form_id     :integer\n#\n\nmodule Wf\n  class Entry < ApplicationRecord\n    belongs_to :form\n    belongs_to :user, class_name: Wf.user_class.to_s\n    belongs_to :workitem\n    has_many :field_values\n\n    after_initialize do\n      self.payload = {} if payload.blank?\n    end\n\n    def json\n      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\n    end\n\n    def for_mini_racer\n      field_values.includes(:field).map { |x| [x.field.name, x.value_after_cast] }.to_h\n    end\n\n    def update_payload!\n      update(payload: json)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/field.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_fields\n#\n#  id              :integer          not null, primary key\n#  name            :string\n#  form_id         :integer\n#  position        :integer          default(\"0\")\n#  field_type      :integer          default(\"0\")\n#  field_type_name :string\n#  default_value   :string\n#  created_at      :datetime         not null\n#  updated_at      :datetime         not null\n#\n\nmodule Wf\n  class Field < ApplicationRecord\n    belongs_to :form, touch: true\n\n    enum field_type: {\n      string: 0,\n      integer: 1,\n      boolean: 2,\n      date: 3,\n      datetime: 4,\n      decimal: 5,\n      float: 6,\n      json: 7,\n      text: 8,\n\n      \"string[]\": 20,\n      \"integer[]\": 21,\n      \"date[]\": 23,\n      \"datetime[]\": 24,\n      \"decimal[]\": 25,\n      \"float[]\": 26,\n      \"json[]\": 27,\n      \"text[]\": 28\n    }\n\n    # TODO: array type\n    def field_type_for_view\n      case field_type\n      when \"string\"\n        \"text_field\"\n      when \"integer\"\n        \"number_field\"\n      when \"date\"\n        \"date_field\"\n      when \"datetime\"\n        \"datetime_field\"\n      when \"boolean\"\n        \"check_box\"\n      when \"text\"\n        \"text_area\"\n      else\n        \"text_field\"\n      end\n    end\n\n    def array?\n      field_type.to_s.match(/^(\\w+)(\\[\\])?$/)[2] == \"[]\"\n    end\n\n    def type_for_cast\n      type = field_type.to_s.match(/^(\\w+)(\\[\\])?$/)[1]\n      if array?\n        ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql, array: true)\n      else\n        ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql)\n      end\n    end\n\n    delegate :cast, to: :type_for_cast\n  end\nend\n"
  },
  {
    "path": "app/models/wf/field_value.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_field_values\n#\n#  id         :integer          not null, primary key\n#  form_id    :integer\n#  field_id   :integer\n#  value      :text\n#  created_at :datetime         not null\n#  updated_at :datetime         not null\n#  entry_id   :integer\n#\n\nmodule Wf\n  class FieldValue < ApplicationRecord\n    belongs_to :form\n    belongs_to :field\n    belongs_to :entry\n\n    def value_after_cast\n      ov = self[:value]\n      if field.array? && !ov.is_a?(Array)\n        v = begin\n              JSON.parse(ov)\n            rescue StandardError\n              []\n            end\n        field.type_for_cast.cast(v)\n      else\n        field.type_for_cast.cast(ov)\n      end\n    end\n\n    def value=(v)\n      self[:value] = if field.array?\n        Array(v.as_json)\n      else\n        v\n      end\n    end\n\n    def value\n      value_after_cast\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/form.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_forms\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nmodule Wf\n  class Form < ApplicationRecord\n    has_many :fields, dependent: :destroy\n    has_many :entries\n  end\nend\n"
  },
  {
    "path": "app/models/wf/group.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_groups\n#\n#  id         :integer          not null, primary key\n#  name       :string\n#  created_at :datetime         not null\n#  updated_at :datetime         not null\n#\n\nmodule Wf\n  class Group < ApplicationRecord\n    has_many :users\n    include Wf::ActsAsParty\n    acts_as_party(user: false, party_name: :name)\n  end\nend\n"
  },
  {
    "path": "app/models/wf/guard.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_guards\n#\n#  id             :integer          not null, primary key\n#  arc_id         :integer\n#  workflow_id    :integer\n#  fieldable_type :string\n#  fieldable_id   :string\n#  op             :string\n#  value          :string\n#  exp            :string\n#  created_at     :datetime         not null\n#  updated_at     :datetime         not null\n#\n\nmodule Wf\n  class Guard < ApplicationRecord\n    belongs_to :workflow\n    belongs_to :arc, touch: true, counter_cache: true\n    belongs_to :fieldable, polymorphic: true, optional: true\n\n    before_validation do\n      self.workflow = arc.workflow\n    end\n\n    validate :validate_exp_and_fieldable\n\n    OP = %w[\n      =\n      >\n      <\n      >=\n      <=\n      is_empty\n    ].freeze\n\n    def value_after_cast\n      field = fieldable\n      fieldable&.cast(value)\n    end\n\n    def pass?(entry, workitem)\n      if exp\n        check_exp(entry, workitem)\n      else\n        check_fieldable(entry)\n      end\n    end\n\n    def check_exp(_entry, workitem)\n      # 1000ms, 200mb\n      context = MiniRacer::Context.new(timeout: 1000, max_memory: 200_000_000)\n      context.eval(\"let target = #{target_hash.to_json};\")\n      context.eval(\"let workitem = #{workitem.to_json};\")\n      exp_value = context.eval(exp)\n      yes_or_no?(exp_value, value)\n    end\n\n    def check_fieldable(entry)\n      fv = entry.field_values.where(field_id: fieldable_id).first\n      return unless fv\n\n      yes_or_no?(fv.value_after_cast, value_after_cast)\n    end\n\n    def yes_or_no?(input_value, setting_value)\n      if op == \"=\"\n        input_value == setting_value\n      elsif op == \">\"\n        input_value > setting_value\n      elsif op == \"<\"\n        input_value < setting_value\n      elsif op == \">=\"\n        input_value >= setting_value\n      elsif op == \"<=\"\n        input_value <= setting_value\n      elsif op == \"is_empty\"\n        input_value.blank?\n      else\n        false\n      end\n    end\n\n    def inspect\n      if exp\n        %(eval(exp) #{op} #{value})\n      else\n        %(#{fieldable&.form&.name}.#{fieldable&.name} #{op} #{value})\n      end\n    end\n\n    def validate_exp_and_fieldable\n      if fieldable && exp.present?\n        errors.add(:exp, \"Exp and Fieldable can not be set at the same time.\")\n        return\n      end\n\n      errors.add(:exp, \"Must set one of Exp and Fieldable.\") unless fieldable || exp.present?\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/lola.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class Lola\n    attr_reader :end_p, :start_p, :workflow\n    def initialize(workflow)\n      @workflow = workflow\n      @start_p  = workflow.places.start.first\n      @end_p    = workflow.places.end.first\n      generate_lola_file!\n    end\n\n    def to_text\n      places      = workflow.places\n      transitions = workflow.transitions\n\n      places_text  = places.map(&:lola_id).join(\",\")\n      marking_text = start_p.lola_id\n      # TODO: with guard\n      transitions_text = transitions.map do |t|\n        consume = t.arcs.in.map { |arc| \"#{arc.place.lola_id}:1\" }.join(\",\")\n        produce = t.arcs.out.map { |arc| \"#{arc.place.lola_id}:1\" }.join(\",\")\n        [\n          \"TRANSITION #{t.lola_id}\",\n          \"CONSUME #{consume};\",\n          \"PRODUCE #{produce};\"\n        ].join(\"\\n\")\n      end.join(\"\\n\\n\")\n\n      <<~LOLA\n        PLACE #{places_text};\n\n        MARKING #{marking_text};\n\n        #{transitions_text}\n      LOLA\n    end\n\n    def json_path(bucket)\n      Rails.root.join(\"tmp\", \"#{workflow.id}-#{bucket}.json\")\n    end\n\n    def lola_path\n      Rails.root.join(\"tmp\", \"#{workflow.id}-#{workflow.updated_at.to_i}.lola\")\n    end\n\n    def generate_lola_file!\n      File.open(lola_path, \"w\") { |f| f.write(Wf::Lola.new(workflow).to_text) } unless File.exist?(lola_path)\n    end\n\n    def soundness?\n      reachability_of_final_marking? && quasiliveness? && !deadlock?\n    end\n\n    def reachability_of_final_marking?\n      formula = workflow.places.reject { |p| p == end_p }.map { |p| \"#{p.lola_id} = 0\" }.join(\" AND \")\n      formula += \" AND #{end_p.lola_id} >= 1\"\n      formula = \"AGEF(#{formula})\"\n      result = run_cmd(formula, \"reachability_of_final_marking\")\n      result.dig(\"analysis\", \"result\")\n    end\n\n    def quasiliveness?\n      workflow.transitions.all? { |t| !dead_transition?(t) }\n    end\n\n    def deadlock?\n      formula = \"EF (DEADLOCK AND (#{end_p.lola_id} = 0))\"\n      result = run_cmd(formula, \"deadlock\")\n      result.dig(\"analysis\", \"result\")\n    end\n\n    private\n\n      def dead_transition?(transition)\n        formula = \"AG NOT FIREABLE (#{transition.lola_id})\"\n        result = run_cmd(formula, \"dead_transition_#{transition.id}\")\n        result.dig(\"analysis\", \"result\")\n      end\n\n      def run_cmd(formula, bucket)\n        cmd = %(lola #{lola_path} --markinglimit=1000 --timelimit=1 --formula=\"#{formula}\" --json=#{json_path(bucket)})\n        $stdout.puts cmd\n        system(cmd)\n        JSON.parse(File.read(json_path(bucket)))\n      end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/multiple_instances/all_finish.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  module MultipleInstances\n    class AllFinish\n      def perform(_workitem)\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/party.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_parties\n#\n#  id            :integer          not null, primary key\n#  partable_type :string\n#  partable_id   :string\n#  party_name    :string\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\nmodule Wf\n  class Party < ApplicationRecord\n    # TODO: use acts_as_partable for sync group or role or user to party\n    belongs_to :partable, polymorphic: true\n    has_many :transition_static_assignments\n    has_many :workitem_assignments\n  end\nend\n"
  },
  {
    "path": "app/models/wf/place.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_places\n#\n#  id          :integer          not null, primary key\n#  workflow_id :integer\n#  name        :string\n#  description :text\n#  sort_order  :integer          default(\"0\")\n#  place_type  :integer          default(\"0\")\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nmodule Wf\n  class Place < ApplicationRecord\n    belongs_to :workflow, touch: true\n    has_many :arcs\n    has_many :tokens\n    enum place_type: {\n      start: 0,\n      normal: 1,\n      end: 2\n    }\n\n    def graph_id\n      \"#{name}/#{id}\"\n    end\n\n    def lola_id\n      \"P#{id}\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/token.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_tokens\n#\n#  id                 :integer          not null, primary key\n#  workflow_id        :integer\n#  case_id            :integer\n#  targetable_type    :string\n#  targetable_id      :string\n#  place_id           :integer\n#  state              :integer          default(\"0\")\n#  locked_workitem_id :integer\n#  produced_at        :datetime\n#  locked_at          :datetime\n#  canceled_at        :datetime\n#  consumed_at        :datetime\n#  created_at         :datetime         not null\n#  updated_at         :datetime         not null\n#\n\nmodule Wf\n  class Token < ApplicationRecord\n    belongs_to :workflow\n    belongs_to :case\n    belongs_to :place\n    belongs_to :locked_workitem, class_name: \"Wf::Workitem\", optional: true\n\n    enum state: {\n      free: 0,\n      locked: 1,\n      canceled: 2,\n      consumed: 3\n    }\n  end\nend\n"
  },
  {
    "path": "app/models/wf/transition.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_transitions\n#\n#  id                    :integer          not null, primary key\n#  name                  :string\n#  description           :text\n#  workflow_id           :integer\n#  sort_order            :integer          default(\"0\")\n#  trigger_limit         :integer\n#  trigger_type          :integer          default(\"0\")\n#  created_at            :datetime         not null\n#  updated_at            :datetime         not null\n#  form_id               :integer\n#  enable_callback       :string           default(\"Wf::Callbacks::EnableDefault\")\n#  fire_callback         :string           default(\"Wf::Callbacks::FireDefault\")\n#  notification_callback :string           default(\"Wf::Callbacks::NotificationDefault\")\n#  time_callback         :string           default(\"Wf::Callbacks::TimeDefault\")\n#  deadline_callback     :string           default(\"Wf::Callbacks::DeadlineDefault\")\n#  hold_timeout_callback :string           default(\"Wf::Callbacks::HoldTimeoutDefault\")\n#  assignment_callback   :string           default(\"Wf::Callbacks::AssignmentDefault\")\n#  unassignment_callback :string           default(\"Wf::Callbacks::UnassignmentDefault\")\n#  form_type             :string           default(\"Wf::Form\")\n#  sub_workflow_id       :integer\n#  multiple_instance     :boolean          default(\"false\")\n#  finish_condition      :string           default(\"Wf::MultipleInstances::AllFinish\")\n#  dynamic_assign_by_id  :integer\n#\n\nmodule Wf\n  class Transition < ApplicationRecord\n    belongs_to :workflow, touch: true\n    has_many :arcs\n    has_many :transition_static_assignments\n    has_many :static_parties, through: :transition_static_assignments, source: \"party\"\n    has_many :workitems\n    belongs_to :form, optional: true, polymorphic: true\n    belongs_to :sub_workflow, optional: true, class_name: \"Wf::Workflow\"\n    belongs_to :dynamic_assign_by, optional: true, class_name: \"Wf::Transition\"\n    has_many :dynamic_assignments, class_name: \"Wf::Transition\", foreign_key: \"dynamic_assign_by_id\"\n\n    enum trigger_type: {\n      user: 0,\n      automatic: 1,\n      message: 2,\n      time: 3\n    }\n\n    validate :validate_trigger_type_and_sub\n\n    def is_sub_workflow?\n      !!sub_workflow_id\n    end\n\n    def explicit_or_split?\n      arcs.out.sum(:guards_count) >= 1\n    end\n\n    validates :name, presence: true\n\n    def validate_trigger_type_and_sub\n      errors.add(:trigger_type, \"sub workflow must have trigger type: automatic, message and time.\") if user? && is_sub_workflow?\n    end\n\n    def graph_id\n      \"#{name}/#{id}\"\n    end\n\n    def lola_id\n      \"T#{id}\"\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/transition_static_assignment.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_transition_static_assignments\n#\n#  id            :integer          not null, primary key\n#  party_id      :integer\n#  transition_id :integer\n#  workflow_id   :integer\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\nmodule Wf\n  class TransitionStaticAssignment < ApplicationRecord\n    belongs_to :workflow\n    belongs_to :transition\n    belongs_to :party\n\n    validates :party_id, uniqueness: { scope: %i[workflow_id transition_id] }\n\n    before_validation do\n      self.workflow_id = transition&.workflow_id\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/user.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_users\n#\n#  id         :integer          not null, primary key\n#  name       :string\n#  created_at :datetime         not null\n#  updated_at :datetime         not null\n#  group_id   :integer\n#\n\nmodule Wf\n  class User < ApplicationRecord\n    belongs_to :group, optional: true\n    include Wf::ActsAsParty\n    acts_as_party(user: true, party_name: :name)\n  end\nend\n"
  },
  {
    "path": "app/models/wf/workflow.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workflows\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :text\n#  is_valid    :boolean          default(\"false\")\n#  creator_id  :string\n#  error_msg   :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\nmodule Wf\n  class Workflow < ApplicationRecord\n    has_many :places, dependent: :destroy\n    has_many :transitions, dependent: :destroy\n    has_many :arcs, dependent: :destroy\n    has_many :transition_static_assignments\n    has_many :cases\n    has_many :workitems\n    has_many :tokens\n\n    validates :name, presence: true\n\n    scope :valid, -> { where(is_valid: true) }\n\n    after_save do\n      do_validate!\n    end\n\n    after_touch do\n      do_validate!\n    end\n\n    # TODO: can from start place to end place\n    # todo: remove color hex to const.\n    def do_validate!\n      msgs = []\n      start_place = places.start.first\n      end_place   = places.end.first\n      msgs << \"must have start place\" if start_place.blank?\n      msgs << \"must have only one start place\" if places.start.count > 1\n      msgs << \"must have end place\" if end_place.blank?\n      msgs << \"must have only one end place\" if places.end.count > 1\n      msgs << \"must not have discrete transition\" if transitions.any? { |t| !t.arcs.in.exists? }\n      if start_place && end_place\n        rgl = to_rgl\n        places.each do |p|\n          msgs << \"start place can not reach #{p.name}\" unless rgl.path?(start_place.to_gid.to_s, p.to_gid.to_s)\n          msgs << \"#{p.name} can not reach end_place\" unless rgl.path?(p.to_gid.to_s, end_place.to_gid.to_s)\n        end\n\n        transitions.each do |t|\n          msgs << \"start place can not reach #{t.name}\" unless rgl.path?(start_place.to_gid.to_s, t.to_gid.to_s)\n          msgs << \"#{t.name} can not reach end_place\" unless rgl.path?(t.to_gid.to_s, end_place.to_gid.to_s)\n        end\n      end\n\n      if Wf.use_lola\n        msgs << \"has deadlock\" if to_lola.deadlock?\n        msgs << \"has dead transition\" unless to_lola.quasiliveness?\n        msgs << \"can not reach to the end place\" unless to_lola.reachability_of_final_marking?\n      end\n\n      if msgs.present?\n        update_columns(is_valid: false, error_msg: msgs.join(\"\\n\"))\n      else\n        update_columns(is_valid: true, error_msg: \"\")\n      end\n    end\n\n    def to_graph(wf_case = nil, base = nil)\n      fontfamily = \"system, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji, Segoe UI Symbol\"\n      fontfamily_monospace = \"SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace\"\n      graph = base || GraphViz.new(name, type: :digraph, rankdir: \"LR\", splines: true, ratio: :auto)\n\n      free_token_places = if wf_case\n        wf_case.tokens.free.map(&:place_id)\n      else\n        []\n      end\n      pg_mapping = {}\n      places.order(\"place_type ASC\").each do |p|\n        if p.start?\n          fillcolor = \"#ffe7ba\"\n          textcolor = \"#fa8c16\"\n          shape     = :doublecircle\n        elsif p.end?\n          fillcolor = \"#dff2ef\"\n          textcolor = \"#29c0b1\"\n          shape     = :doublecircle\n        else\n          fillcolor = \"#fbdbe1\"\n          textcolor = \"#ff3366\"\n          shape     = :circle\n        end\n\n        token_count = free_token_places.count(p.id)\n        if token_count >= 1\n          label  = \"&bull;\"\n          xlabel = nil\n          fontsize = 25\n        else\n          label = p.name\n          xlabel = nil\n        end\n\n        pg = graph.add_nodes(p.graph_id,\n                             label: label,\n                             xlabel: xlabel,\n                             shape: shape,\n                             fixedsize: true,\n                             style: :filled,\n                             fontname: fontfamily,\n                             fontcolor: textcolor,\n                             fontsize: fontsize,\n                             color: fillcolor,\n                             fillcolor: fillcolor,\n                             href: Wf::Engine.routes.url_helpers.edit_workflow_place_path(self, p))\n        pg_mapping[p] = pg\n      end\n\n      tg_mapping = {}\n      transitions.each do |t|\n        peripheries = if t.multiple_instance?\n          3\n        else\n          1\n        end\n        tg = graph.add_nodes(t.graph_id, label: t.name, shape: :box, style: :filled, fillcolor: \"#d6ddfa\", color: \"#d6ddfa\",\n                                         fontcolor: \"#2c50ed\", fontname: fontfamily, peripheries: peripheries,\n                                         href: Wf::Engine.routes.url_helpers.edit_workflow_transition_path(self, t))\n        tg_mapping[t] = tg\n        # NOTICE: if sub_workflow is transition's workflow, then graph will loop infinite, this is valid for workflow definition.\n        next unless t.is_sub_workflow? && t.sub_workflow != t.workflow\n\n        sub_graph = graph.add_graph(\"cluster#{t.sub_workflow_id}\", rankdir: \"LR\", splines: true, ratio: :auto)\n        sub_graph[:label] = t.sub_workflow.name\n        sub_graph[:style] = :dashed\n        sub_graph[:color] = :lightgrey\n        # TODO: detect related case for sub workflow.\n        t.sub_workflow.to_graph(nil, sub_graph)\n        graph.add_edges(tg, t.sub_workflow.places.start.first.graph_id, style: :dashed, dir: :both)\n      end\n\n      arcs.order(\"direction desc\").each do |arc|\n        label = if arc.guards_count > 0\n          arc.guards.map(&:inspect).join(\" & \")\n        else\n          \"\"\n        end\n        if arc.in?\n          graph.add_edges(\n            pg_mapping[arc.place],\n            tg_mapping[arc.transition],\n            label: label,\n            weight: 1,\n            labelfloat: false,\n            labelfontcolor: :red,\n            arrowhead: :vee,\n            fontsize: 10,\n            color: \"#53585c\",\n            fontcolor: \"#53585c\",\n            fontname: fontfamily_monospace,\n            href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)\n          )\n        else\n          graph.add_edges(\n            tg_mapping[arc.transition],\n            pg_mapping[arc.place],\n            label: label,\n            weight: 1,\n            labelfloat: false,\n            labelfontcolor: :red,\n            arrowhead: :vee,\n            fontsize: 10,\n            color: \"#53585c\",\n            fontcolor: \"#53585c\",\n            fontname: fontfamily_monospace,\n            href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)\n          )\n        end\n      end\n      graph\n    end\n\n    def render_graph(wf_case = nil)\n      graph = to_graph(wf_case)\n      path = Rails.root.join(\"tmp\", \"#{id}.svg\")\n      graph.output(svg: path)\n      File.read(path)\n    end\n\n    def to_lola\n      @lola ||= Wf::Lola.new(self)\n    end\n\n    def to_rgl\n      graph = RGL::DirectedAdjacencyGraph.new\n      places.order(\"place_type ASC\").each do |p|\n        graph.add_vertex(p.to_gid.to_s)\n      end\n\n      transitions.each do |t|\n        graph.add_vertex(t.to_gid.to_s)\n      end\n\n      arcs.order(\"direction desc\").each do |arc|\n        if arc.in?\n          graph.add_edge(arc.place.to_gid.to_s, arc.transition.to_gid.to_s)\n        else\n          graph.add_edge(arc.transition.to_gid.to_s, arc.place.to_gid.to_s)\n        end\n      end\n      graph\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/workitem.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workitems\n#\n#  id                      :integer          not null, primary key\n#  case_id                 :integer\n#  workflow_id             :integer\n#  transition_id           :integer\n#  state                   :integer          default(\"0\")\n#  enabled_at              :datetime\n#  started_at              :datetime\n#  canceled_at             :datetime\n#  finished_at             :datetime\n#  overridden_at           :datetime\n#  deadline                :datetime\n#  created_at              :datetime         not null\n#  updated_at              :datetime         not null\n#  trigger_time            :datetime\n#  holding_user_id         :string\n#  children_count          :integer          default(\"0\")\n#  children_finished_count :integer          default(\"0\")\n#  forked                  :boolean          default(\"false\")\n#  parent_id               :integer\n#\n\nmodule Wf\n  class Workitem < ApplicationRecord\n    belongs_to :workflow\n    belongs_to :transition\n    belongs_to :case\n    belongs_to :parent, class_name: \"Wf::Workitem\", optional: true, counter_cache: :children_count\n    belongs_to :holding_user, foreign_key: :holding_user_id, class_name: Wf.user_class.to_s, optional: true\n    has_many :workitem_assignments\n    has_many :parties, through: :workitem_assignments, source: \"party\"\n    has_many :comments\n    has_many :entries, class_name: Wf.entry_class.to_s\n    has_one :started_case, foreign_key: :started_by_workitem_id, class_name: \"Wf::Case\"\n\n    has_many :children, foreign_key: :parent_id, class_name: \"Wf::Workitem\"\n\n    enum state: {\n      enabled: 0,\n      started: 1,\n      canceled: 2,\n      finished: 3,\n      overridden: 4\n    }\n\n    def self.todo(wf_current_user)\n      current_party_ids = [\n        wf_current_user,\n        Wf.org_classes.map { |org, _org_class| wf_current_user&.public_send(org) }\n      ].flatten.map { |x| x&.party&.id }.compact\n      Wf::Workitem.where(forked: false).joins(:workitem_assignments).where(Wf::WorkitemAssignment.table_name => { party_id: current_party_ids })\n    end\n\n    def self.doing(wf_current_user)\n      where(holding_user: wf_current_user).where(state: %i[started enabled])\n    end\n\n    def self.done(wf_current_user)\n      where(holding_user: wf_current_user).where(state: [:finished])\n    end\n\n    def for_mini_racer\n      attr = attributes\n      attr = attr.merge(holding_user: holding_user&.attributes || {})\n      attr = attr.merge(form: entries.to_a.first&.for_mini_racer || {})\n      children_attrs = if forked?\n        []\n      else\n        children.includes(:holding_user, entries: :field_values).map(&:for_mini_racer)\n      end\n      attr = attr.merge(children: children_attrs)\n      attr\n    end\n\n    def parent?\n      !forked\n    end\n\n    def name\n      \"Workitem -> #{id}\"\n    end\n\n    def pass_guard?(arc, has_passed = false)\n      if arc.guards_count == 0\n        !has_passed\n      else\n        entry = entries.where(user: holding_user).first\n        arc.guards.all? { |guard| guard.pass?(entry, self) }\n      end\n    end\n\n    def real?\n      return false if transition.multiple_instance? && parent_id.nil?\n      return false if transition.sub_workflow_id.present?\n\n      true\n    end\n\n    def started_by?(user)\n      real? && enabled? && owned_by?(user)\n    end\n\n    def finished_by?(user)\n      real? && started? && owned_by?(user) && holding_user == user\n    end\n\n    def owned_by?(user)\n      Wf::Party.joins(workitem_assignments: { workitem: %i[transition case] })\n               .where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:user] })\n               .where(Wf::Case.table_name => { state: Wf::Case.states[:active] })\n               .where(Wf::Workitem.table_name => { state: Wf::Workitem.states.values_at(:started, :enabled) })\n               .where(Wf::Workitem.table_name => { id: id }).map do |party|\n        party.partable.users.to_a\n      end.flatten.include?(user)\n    end\n  end\nend\n"
  },
  {
    "path": "app/models/wf/workitem_assignment.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workitem_assignments\n#\n#  id          :integer          not null, primary key\n#  party_id    :integer\n#  workitem_id :integer\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nmodule Wf\n  class WorkitemAssignment < ApplicationRecord\n    belongs_to :party\n    belongs_to :workitem\n  end\nend\n"
  },
  {
    "path": "app/views/layouts/wf/_alert.html.erb",
    "content": "<% return if alert.blank? %>\n<div id=\"alert\" class=\"alert alert-warning\">\n  <p>\n    <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>\n</div>\n"
  },
  {
    "path": "app/views/layouts/wf/_footer.html.erb",
    "content": "<footer class=\"footer\">\n  <div class=\"container\">\n    <p>\n      <strong>\n        <a href=\"https://github.com/hooopo/petri_flow\" target=\"_blank\" rel=\"nofollow\">Petri Flow</a>\n      </strong>\n      crafted by <a href=\"https://github.com/hooopo\" target=\"_blank\" rel=\"nofollow\">Hooopo</a>.\n      The source code is licensed under <a href=\"http://opensource.org/licenses/mit-license.php\">MIT license</a>.\n    </p>\n  </div>\n</footer>\n"
  },
  {
    "path": "app/views/layouts/wf/_nav.html.erb",
    "content": "<nav class=\"navbar navbar-petri\" role=\"navigation\" aria-label=\"main navigation\">\n  <div class=\"container\">\n    <div class=\"navbar-brand\">\n      <%= link_to \"👨‍💻Petri Flow\", root_path, class: \"navbar-item\" %>\n    </div>\n\n    <div class=\"navbar-end\">\n      <%= link_to wf_current_user&.name, workitems_path %>\n      <%= link_to 'Forms', forms_path, class: \"navbar-item #{\"is-active\" if controller_name == \"forms\"}\" %>\n      <%= link_to 'Workflows', workflows_path, class: \"navbar-item #{\"is-active\" if controller_name.start_with?(\"workflows\")}\" %>\n    </div>\n  </div>\n</nav>\n"
  },
  {
    "path": "app/views/layouts/wf/_notice.html.erb",
    "content": "<% return if notice.blank? %>\n<div id=\"notice\" class=\"alert alert-success\">\n  <p>\n    <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>\n</div>\n"
  },
  {
    "path": "app/views/layouts/wf/application.html.erb",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <title>Petri Flow</title>\n\n  <%= csrf_meta_tags %>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1.0\"/>\n\n  <%= stylesheet_link_tag    \"wf/application\", media: \"all\", \"data-turbolinks-track\": \"reload\" %>\n  <%= javascript_include_tag \"wf/application\", \"data-turbolinks-track\": \"reload\" %>\n</head>\n<body>\n\n<%= render \"layouts/wf/nav\" %>\n<main class=\"container\">\n  <%= render \"layouts/wf/notice\" %>\n  <%= render \"layouts/wf/alert\" %>\n  <!--\n  <nav aria-label=\"breadcrumb\">\n  <ol class='breadcrumb'>\n    <% breadcrumb_trail do |crumb| %>\n      <li class=\"breadcrumb-item <%= crumb.current? ? 'active' : '' %>\">\n        <%= link_to crumb.name, crumb.url, (crumb.current? ? {'aria-current' => 'page'} : {}) %>\n      </li>\n    <% end %>\n  </ol>\n</nav>\n-->\n  <%= content_for?(:content) ? yield(:content) : yield %>\n</main>\n\n<%= render \"layouts/wf/footer\" %>\n</body>\n</html>\n"
  },
  {
    "path": "app/views/wf/arcs/_form.html.erb",
    "content": "<%= form_with(model: arc, url: [@workflow, @arc], local: true) do |f| %>\n  <% if arc.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(arc.errors.count, \"error\") %> prohibited this arc from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% arc.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :place, class: \"label\" %>\n    <%= f.select :place_id, @workflow.places.map{|x| [x.name, x.id]}, {}, class: \"form-control custom-select\", placeholder: \"Place\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :transition, class: \"label\" %>\n    <%= f.select :transition_id, @workflow.transitions.map{|x| [x.name, x.id]}, {}, class: \"custom-select\", placeholder: \"Transition\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <div id=\"notice\" class=\"alert alert-success\">\n      Direction is base on transition, in: P->T, out: T->P.\n    </div>\n    <%= f.label :direction, class: \"label\" %>\n    <%= f.select :direction, Wf::Arc.directions.keys, {}, class: \"form-control custom-select\", placeholder: \"Direction\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/arcs/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, arc: @arc %>\n</div>\n"
  },
  {
    "path": "app/views/wf/arcs/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, arc: @arc %>\n</div>\n"
  },
  {
    "path": "app/views/wf/arcs/show.html.erb",
    "content": "<div class=\"card card-body\">\n<div class=\"float-right\">\n  <%= link_to 'Delete Arc', workflow_path(@workflow, @arc), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger' %>\n  <%= link_to 'Edit Arc', edit_workflow_arc_path(@workflow, @arc), class: 'btn btn-primary' %>\n  <% if @arc.out? %>\n    <%= link_to 'Create Guards', new_arc_guard_path(@arc), class: 'btn btn-primary' %>\n  <% end %>\n</div>\n<div>\n  <h2>Arc Detail</h2>\n  <table class=\"table table-view\">\n    <tbody>\n      <tr>\n        <th scope=\"row\">ID</th>\n        <td><%= @arc.id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Name</th>\n        <td><%= @arc.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Transition</th>\n        <td><%= @arc.transition.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Place</th>\n        <td><%= @arc.place.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Guards</th>\n        <td><%= @arc.guards.map {|x| x.inspect }.join(\" & \") %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Direction</th>\n        <td><%= @arc.direction %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Guards</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Field</th>\n        <th scope=\"col\">Op</th>\n        <th scope=\"col\">Value</th>\n        <th scope=\"col\">Exp</th>\n        <th scope=\"col\">Actions</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @arc.guards.each do |guard| %>\n        <tr>\n          <td><%= guard.id %></td>\n          <td><%= guard.fieldable&.form&.name %>/<%= guard.fieldable&.name %></td>\n          <td><%= guard.op %></td>\n          <td><%= guard.value %></td>\n          <td>\n            <pre>\n              <%= guard.exp %>\n            </pre>\n          </td>\n          <td><%= link_to 'Edit Guard', edit_arc_guard_path(@arc, guard), class: 'btn btn-sm btn-info' %></td>\n          <td><%= link_to 'Delete Guard', [@arc, guard], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-info' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/cases/_form.html.erb",
    "content": "<%= form_with(model: wf_case, url: [@workflow, @wf_case], local: true) do |f| %>\n  <% if wf_case.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(wf_case.errors.count, \"error\") %> prohibited this wf_case from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% wf_case.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :targetable, class: \"label\" %>\n    <%= 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\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/cases/index.html.erb",
    "content": "<div class=\"card card-body\">\n  <div class=\"float-left\"><h2>Cases</h2></div>\n  <div class=\"float-right\">\n    <%= link_to 'New Case', new_workflow_case_path(@workflow), class: 'btn btn-primary' %>\n  </div>\n  <div>\n    <table class=\"table table-view\">\n      <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Created At</th>\n        <th scope=\"col\">Targetable Type</th>\n        <th scope=\"col\">Targetable ID</th>\n        <th scope=\"col\">Action</th>\n      </tr>\n      </thead>\n      <tbody>\n      <% @cases.each do |wf_case| %>\n        <tr>\n          <td><%= wf_case.id %></td>\n          <td><%= wf_case.state %></td>\n          <td><%= wf_case.created_at %></td>\n          <td><%= wf_case.targetable_type %></td>\n          <td><%= wf_case.targetable_id %></td>\n          <td>\n            <%= link_to \"Run\", workflow_case_path(@workflow, wf_case), class: 'btn btn-success btn-sm' %>\n          </td>\n        </tr>\n      <% end %>\n      </tbody>\n    </table>\n  </div>\n  <div>\n    <%= paginate @cases, theme: 'twitter-bootstrap-4' %>\n  </div>\n</div>\n"
  },
  {
    "path": "app/views/wf/cases/new.html.erb",
    "content": "<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",
    "content": "<div class=\"card card-body\">\n<div>\n  <h2>Case Detail</h2>\n  <table class=\"table table-view\">\n    <tbody>\n      <tr>\n        <th scope=\"row\">ID</th>\n        <td><%= @wf_case.id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Workflow</th>\n        <td><%= link_to @wf_case.workflow.name, workflow_path(@wf_case.workflow) %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">State</th>\n        <td><%= @wf_case.state %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Targetable Type</th>\n        <td><%= @wf_case.targetable_type %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Targetable ID</th>\n        <td><%= @wf_case.targetable_id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Created At</th>\n        <td><%= @wf_case.created_at %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Case Graph</h2>\n  <div class=\"card\">\n    <div class=\"card-body\">\n      <%=raw @wf_case.workflow.render_graph(@wf_case) %>\n    </div>\n  </div>\n</div>\n\n<div>\n  <h2>Tokens</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Place</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Locked Workitem</th>\n        <th scope=\"col\">Produced At</th>\n        <th scope=\"col\">Locked At</th>\n        <th scope=\"col\">Canceled At</th>\n        <th scope=\"col\">Consumed At</th>\n        <th scope=\"col\">Created At</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @wf_case.tokens.each do |token| %>\n        <tr>\n          <td><%= token.id %></td>\n          <td><%= link_to token.place.name, workflow_place_path(token.workflow, token.place) %> </td>\n          <td><%= token.state %></td>\n          <td><%= token.locked_workitem_id %></td>\n          <td><%= token.produced_at %></td>\n          <td><%= token.locked_at %></td>\n          <td><%= token.canceled_at %></td>\n          <td><%= token.consumed_at %></td>\n          <td><%= token.created_at %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Workitems</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Transition</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Holding User</th>\n        <th scope=\"col\">Started At</th>\n        <th scope=\"col\">Enabled At</th>\n        <th scope=\"col\">Canceled At</th>\n        <th scope=\"col\">Finished At</th>\n        <th scope=\"col\">Overridden At</th>\n        <th scope=\"col\">Deadline</th>\n        <th scope=\"col\">Detail</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @wf_case.workitems.each do |workitem| %>\n        <tr>\n          <td><%= link_to workitem.id, workitem_path(workitem) %></td>\n          <td>\n            <%= link_to workitem.transition.name, workflow_transition_path(workitem.workflow, workitem.transition) %> </td>\n          <td><%= workitem.state %></td>\n          <td><%= workitem.holding_user_id %></td>\n          <td><%= workitem.started_at %></td>\n          <td><%= workitem.enabled_at %></td>\n          <td><%= workitem.canceled_at %></td>\n          <td><%= workitem.finished_at %></td>\n          <td><%= workitem.overridden_at %></td>\n          <td><%= workitem.deadline %></td>\n          <td><%= link_to \"Run\", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/comments/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= form_with(model: @comment, url: workitem_comments_path(@workitem), local: true) do |f| %>\n  <% if @comment.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(@comment.errors.count, \"error\") %> prohibited this comment from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% @comment.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :body, class: \"label\" %>\n    <%= f.text_area :body, class: \"form-control\", rows: 3, placeholder: \"comment\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", value: \"Comment\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>\n</div>\n"
  },
  {
    "path": "app/views/wf/fields/_form.html.erb",
    "content": "<%= form_with(model: field, url: [@form, field], local: true) do |f| %>\n  <% if field.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(field.errors.count, \"error\") %> prohibited this field from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% field.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :name, class: \"label\" %>\n    <%= f.text_field :name, class: \"form-control\", fieldholder: \"Name\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :position, class: \"label\" %>\n    <%= f.text_field :position, class: \"form-control\", fieldholder: \"Position\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :default_value, class: \"label\" %>\n    <%= f.text_field :default_value, class: \"form-control\", fieldholder: \"Default Value\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :field_type, class: \"label\" %>\n    <%= f.select :field_type, Wf::Field.field_types.keys, {}, class: \"form-control custom-select\", fieldholder: \"Field Type\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/fields/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", form: @form, field: @field %>\n</div>\n"
  },
  {
    "path": "app/views/wf/fields/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", form: @form, field: @field %>\n</div>\n"
  },
  {
    "path": "app/views/wf/forms/_form.html.erb",
    "content": "<%= form_with(model: form, local: true) do |f| %>\n  <% if form.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(form.errors.count, \"error\") %> prohibited this form from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% form.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :name, class: \"label\" %>\n    <%= f.text_field :name, class: \"form-control\", placeholder: \"Name\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :description, class: \"label\" %>\n    <%= f.text_area :description, class: \"form-control\", placeholder: \"Description\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/forms/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", form: @form %>\n</div>\n"
  },
  {
    "path": "app/views/wf/forms/index.html.erb",
    "content": "<div class=\"card card-body\">\n <div class=\"d-flex  justify-content-between\">\n   <div><h2>Forms</h2></div>\n   <div><%= link_to '+  New Form', new_form_path, class: 'btn btn-primary' %></div>\n </div>\n<div>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Description</th>\n        <th scope=\"col\"></th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @forms.each do |form| %>\n        <tr>\n          <td><%= form.id %></td>\n          <td><%= link_to form.name, form_path(form) %></td>\n          <td><%= form.description %></td>\n          <td class=\"text-right\">\n            <%= link_to 'Delete Form', form_path(form), method: :delete, data: {confirm: 'confirm?'}, class: 'btn-link btn btn-sm text-danger' %>\n            <%= link_to 'Create Field', new_form_field_path(form), class: 'btn btn-link btn-sm' %>\n          </td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n<div>\n  <%= paginate @forms, theme: 'twitter-bootstrap-4' %>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/forms/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", form: @form %>\n</div>\n"
  },
  {
    "path": "app/views/wf/forms/show.html.erb",
    "content": "<div class=\"card card-body\">\n<div class=\"d-flex  justify-content-end\">\n  <%= link_to 'Delete Form', form_path(@form), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger mr-2' %>\n  <%= link_to 'Edit Form', edit_form_path(@form), class: 'btn btn-light mr-2' %>\n  <%= link_to 'Create Fields', new_form_field_path(@form), class: 'btn btn-light' %>\n</div>\n<div>\n  <h2>Form Detail</h2>\n  <table class=\"table table-view\">\n    <tbody>\n      <tr>\n        <th scope=\"row\">ID</th>\n        <td><%= @form.id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Name</th>\n        <td><%= @form.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Description</th>\n        <td><%= @form.description %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Fields</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Position</th>\n        <th scope=\"col\">Field Type</th>\n        <th scope=\"col\">Default Value</th>\n        <th scope=\"col\"></th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @form.fields.each do |field| %>\n        <tr>\n          <td><%= field.id %></td>\n          <td><%= field.name %></td>\n          <td><%= field.position %></td>\n          <td><%= field.field_type %></td>\n          <td><%= field.default_value %></td>\n          <td class=\"text-right\"><%= link_to 'Edit Field', edit_form_field_path(@form, field), class: 'btn btn-sm btn-link mr-2' %>\n            <%= link_to 'Delete Field', [@form, field], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/guards/_form.html.erb",
    "content": "<%= form_with(model: guard, url: [@arc, @guard], local: true) do |f| %>\n  <% if guard.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(guard.errors.count, \"error\") %> prohibited this guard from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% guard.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :fieldable, class: \"label\" %>\n    <%= 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\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= 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>\n    <%= f.text_area :exp, class: \"form-control\", placeholder: \"Exp\", rows: 8 %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :op, class: \"label\" %>\n    <%= f.select :op, Wf::Guard::OP, {}, class: \"form-control custom-select\", placeholder: \"Op\" %>\n  </div>\n\n\n  <div class=\"form-group\">\n    <%= f.label :value, class: \"label\" %>\n    <%= f.text_field :value, class: \"form-control\", placeholder: \"Value\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/guards/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", arc: @arc, guard: @guard %>\n</div>\n"
  },
  {
    "path": "app/views/wf/guards/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", arc: @arc, guard: @guard %>\n</div>\n"
  },
  {
    "path": "app/views/wf/places/_form.html.erb",
    "content": "<%= form_with(model: place, url: [@workflow, @place], local: true) do |f| %>\n  <% if place.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(place.errors.count, \"error\") %> prohibited this place from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% place.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :name, class: \"label\" %>\n    <%= f.text_field :name, class: \"form-control\", placeholder: \"Name\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :description, class: \"label\" %>\n    <%= f.text_area :description, class: \"form-control\", placeholder: \"Description\" %>\n  </div>\n\n\n  <div class=\"form-group\">\n    <%= f.label :sort_order, class: \"label\" %>\n    <%= f.text_field :sort_order, class: \"form-control\", placeholder: \"Sort Order\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :place_type, class: \"label\" %>\n    <%= f.select :place_type, Wf::Place.place_types.keys, {}, class: \"form-control custom-select\", placeholder: \"Place Type\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/places/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, place: @place %>\n</div>\n"
  },
  {
    "path": "app/views/wf/places/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, place: @place %>\n</div>\n"
  },
  {
    "path": "app/views/wf/static_assignments/_form.html.erb",
    "content": "<%= form_with(model: static_assignment, url: transition_static_assignments_path(static_assignment.transition), local: true) do |f| %>\n  <% if static_assignment.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(static_assignment.errors.count, \"error\") %> prohibited this static_assignment from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% static_assignment.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :party, class: \"label\" %>\n    <%= f.select :party_id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: \"form-control custom-select\", placeholder: \"fieldable\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", value: 'Create Static Assignment', data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/static_assignments/new.html.erb",
    "content": "<div class=\"card card-body\">\n  <%= render \"form\", static_assignment: @static_assignment %>\n</div>\n"
  },
  {
    "path": "app/views/wf/transitions/_form.html.erb",
    "content": "<%= form_with(model: transition, url: [@workflow, @transition], local: true) do |f| %>\n  <% if transition.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(transition.errors.count, \"error\") %> prohibited this transition from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% transition.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Basic Information</h5>\n    <div class=\"card-body\">\n      <div class=\"form-group\">\n        <%= f.label :name, class: \"label\" %>\n        <%= f.text_field :name, class: \"form-control\", placeholder: \"Name\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :description, class: \"label\" %>\n        <%= f.text_area :description, class: \"form-control\", placeholder: \"Description\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :trigger_limit, class: \"label\" %>\n        <%= f.text_field :trigger_limit, class: \"form-control\", placeholder: \"Trigger Limit\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :trigger_type, class: \"label\" %>\n        <%= f.select :trigger_type, Wf::Transition.trigger_types.keys, {}, class: \"form-control custom-select\", placeholder: \"Trigger Type\" %>\n      </div>\n    </div>\n  </div>\n\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Sub Workflow</h5>\n  <div class=\"card-body\">\n    <div class=\"form-group\">\n      <%= f.label :sub_workflow, class: \"label\" %>\n      <%= 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\" %>\n    </div>\n  </div>\n  </div>\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Dynimac Form</h5>\n    <div class=\"card-body\">\n      <div class=\"form-group\">\n        <%= f.label :form, class: \"label\" %>\n        <%= 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\" %>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Dynamic Assign By</h5>\n  <div class=\"card-body\">\n    <div class=\"form-group\">\n      <%= f.label :dynamic_assign_by, class: \"label\" %>\n      <%= 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\" %>\n    </div>\n  </div>\n  </div>\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Callbacks</h5>\n    <div class=\"card-body\">\n\n      <div class=\"form-group\">\n        <%= f.label :enable_callback, class: \"label\" %>\n        <%= f.select :enable_callback, Wf.enable_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :fire_callback, class: \"label\" %>\n        <%= f.select :fire_callback, Wf.fire_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :deadline_callback, class: \"label\" %>\n        <%= f.select :deadline_callback, Wf.deadline_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :time_callback, class: \"label\" %>\n        <%= f.select :time_callback, Wf.time_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :hold_timeout_callback, class: \"label\" %>\n        <%= f.select :hold_timeout_callback, Wf.hold_timeout_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :assignment_callback, class: \"label\" %>\n        <%= f.select :assignment_callback, Wf.assignment_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :unassignment_callback, class: \"label\" %>\n        <%= f.select :unassignment_callback, Wf.unassignment_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n\n      <div class=\"form-group\">\n        <%= f.label :notification_callback, class: \"label\" %>\n        <%= f.select :notification_callback, Wf.notification_callbacks, {}, class: \"form-control custom-select\", placeholder: \"Callback\" %>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"card\">\n  <h5 class=\"card-header\">Multiple Instances</h5>\n    <div class=\"card-body\">\n      <div class=\"form-group\">\n        <%= f.label :multiple_instance, class: \"label\" %>\n        <%= f.check_box :multiple_instance %>\n      </div>\n      <div class=\"form-group\">\n        <%= f.label :finish_condition, class: \"label\" %>\n        <%= f.select :finish_condition, Wf.finish_conditions, {}, class: \"form-control custom-select\", placeholder: \"Finish_condition\" %>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/transitions/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, transition: @transition %>\n</div>\n"
  },
  {
    "path": "app/views/wf/transitions/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow, transition: @transition %>\n</div>\n"
  },
  {
    "path": "app/views/wf/transitions/show.html.erb",
    "content": "<div class=\"card card-body\">\n<div class=\"float-right\">\n  <%= link_to 'Edit Transition', edit_workflow_transition_path(@workflow, @transition), class: 'btn btn-primary' %>\n  <%= link_to 'Create Static Assignments', new_transition_static_assignment_path(@transition), class: 'btn btn-primary' %>\n</div>\n<div>\n  <h2>Transition Detail</h2>\n  <table class=\"table table-view\">\n    <tbody>\n      <tr>\n        <th scope=\"row\">ID</th>\n        <td><%= @transition.id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Name</th>\n        <td><%= @transition.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Trigger Limit</th>\n        <td><%= @transition.trigger_limit %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Trigger Type</th>\n        <td><%= @transition.trigger_type %></td>\n      </tr>\n      <tr>\n        <th>Form</th>\n        <td>\n          <% if @transition.form %>\n             <%= link_to @transition.form.name, form_path(@transition.form) %>\n          <% else %>\n             No Form\n          <% end %>\n        </td>\n      </tr>\n\n      <tr>\n        <th>Sub Workflow</th>\n        <td>\n            <% if @transition.sub_workflow %>\n              <%= link_to @transition.sub_workflow.name, workflow_path(@transition.sub_workflow_id) %>\n            <% else %>\n              No\n            <% end %>\n        </td>\n      </tr>\n\n      <tr>\n        <th>Dynamic Assign By</th>\n        <td>\n            <% if @transition.dynamic_assign_by %>\n              <%= link_to @transition.dynamic_assign_by.name, workflow_transition_path(@workflow, @transition.dynamic_assign_by_id) %>\n            <% else %>\n              No\n            <% end %>\n        </td>\n      </tr>\n\n      <tr>\n        <th scope=\"row\">Multiple Instance?</th>\n        <td><%= @transition.multiple_instance %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Finish Condition</th>\n        <td><%= @transition.finish_condition %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Static Assignments</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Party Name</th>\n        <th scope=\"col\">Party Type</th>\n        <th scope=\"col\">Actions</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @transition.transition_static_assignments.each do |assign| %>\n        <tr>\n          <td><%= assign.id %></td>\n          <td><%= assign.party.party_name %></td>\n          <td><%= assign.party.partable_type %></td>\n          <td><%= link_to 'Delete', transition_static_assignment_path(@transition, assign), remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-info' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/workflows/_form.html.erb",
    "content": "<%= form_with(model: workflow, local: true) do |f| %>\n  <% if workflow.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(workflow.errors.count, \"error\") %> prohibited this workflow from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% workflow.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :name, class: \"label\" %>\n    <%= f.text_field :name, class: \"form-control\", placeholder: \"Name\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.label :description, class: \"label\" %>\n    <%= f.text_area :description, class: \"form-control\", placeholder: \"Description\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>"
  },
  {
    "path": "app/views/wf/workflows/edit.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow %>\n</div>\n"
  },
  {
    "path": "app/views/wf/workflows/index.html.erb",
    "content": "<div class=\"card card-body\">\n  <div class=\"d-flex  justify-content-between\">\n\n  <div><h2>Workflows</h2></div>\n<div>\n  <%= link_to 'New Workflow', new_workflow_path, class: 'btn btn-primary' %>\n</div>\n  </div>\n<div>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Description</th>\n        <th scope=\"col\">Is Valid?</th>\n        <th scope=\"col\">Error Msg</th>\n        <th scope=\"col\"></th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workflows.each do |workflow| %>\n        <tr>\n          <td><%= workflow.id %></td>\n          <td><%= link_to workflow.name, workflow_path(workflow) %></td>\n          <td><%= workflow.description %></td>\n          <td><%= workflow.is_valid? %></td>\n          <td>\n            <pre>\n              <%= workflow.error_msg %>\n            </pre>\n          </td>\n          <td class=\"text-right\">\n            <%= link_to 'Delete Workflow', workflow_path(workflow), remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-link btn-sm text-danger' %>\n            <%= link_to 'Create Transition', new_workflow_transition_path(workflow), class: 'btn btn-link btn-sm' %>\n            <%= link_to 'Create Place', new_workflow_place_path(workflow), class: 'btn btn-link btn-sm' %>\n            <%= link_to 'Create Arc', new_workflow_arc_path(workflow), class: 'btn btn-link btn-sm' %>\n          </td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n<div>\n  <%= paginate @workflows, theme: 'twitter-bootstrap-4' %>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/workflows/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= render \"form\", workflow: @workflow %>\n</div>\n"
  },
  {
    "path": "app/views/wf/workflows/show.html.erb",
    "content": "<div class=\"d-flex  justify-content-end\">\n<% if @workflow.is_valid? %>\n    <%= link_to 'New Case', new_workflow_case_path(@workflow), class: 'btn btn-primary mr-2' %>\n  <% end %>\n  <%= link_to 'Delete Workflow', workflow_path(@workflow), data: {confirm: 'confirm?'}, method: :delete, class: 'btn btn-danger mr-2' %>\n  <%= link_to 'Edit Workflow', edit_workflow_path(@workflow), class: 'btn btn-light mr-2' %>\n  <%= link_to 'Create Transitions', new_workflow_transition_path(@workflow), class: 'btn btn-light mr-2' %>\n  <%= link_to 'Create Places', new_workflow_place_path(@workflow), class: 'btn btn-light mr-2' %>\n  <%= link_to 'Create Arcs', new_workflow_arc_path(@workflow), class: 'btn btn-light ' %>\n</div>\n<div class=\"card card-body\">\n  <h2 class=\"\">Workflow Detail</h2>\n  <table class=\"table table-view\">\n    <tbody>\n      <tr>\n        <th scope=\"row\">ID</th>\n        <td><%= @workflow.id %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Name</th>\n        <td><%= @workflow.name %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Description</th>\n        <td><%= @workflow.description %></td>\n      </tr>\n      <tr>\n        <th scope=\"row\">Is Valid?</th>\n        <td><code><%= @workflow.is_valid? %></code></td>\n      </tr>\n\n      <tr>\n        <th scope=\"row\">Error Msg</th>\n        <td>\n          <pre>\n            <%= @workflow.error_msg %>\n          </pre>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div class=\"card card-body\">\n  <h2>Graph Editor</h2>\n  <div class=\"card\">\n    <div class=\"card-body\">\n      <%=raw @workflow.render_graph %>\n    </div>\n  </div>\n</div>\n\n<div class=\"card card-body\">\n  <h2 class=\"\">Cases</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">All</th>\n        <th scope=\"col\">Created</th>\n        <th scope=\"col\">Active</th>\n        <th scope=\"col\">Suspended</th>\n        <th scope=\"col\">Canceled</th>\n        <th scope=\"col\">Finished</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><%= link_to @workflow.cases.count, workflow_cases_path(@workflow) %></td>\n        <td><%= link_to @workflow.cases.created.count, workflow_cases_path(@workflow, state: :created) %></td>\n        <td><%= link_to @workflow.cases.active.count, workflow_cases_path(@workflow, state: :active) %></td>\n        <td><%= link_to @workflow.cases.suspended.count, workflow_cases_path(@workflow, state: :suspended) %></td>\n        <td><%= link_to @workflow.cases.canceled.count, workflow_cases_path(@workflow, state: :canceled) %></td>\n        <td><%= link_to @workflow.cases.finished.count, workflow_cases_path(@workflow, state: :finished) %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div class=\"card card-body\">\n  <h2>Places</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Description</th>\n        <th scope=\"col\">Place Type</th>\n        <th scope=\"col\">Sort Order</th>\n        <th scope=\"col\"></th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workflow.places.each do |place| %>\n        <tr>\n          <td><%= place.id %></td>\n          <td><%= place.name %></td>\n          <td><%= place.description %></td>\n          <td><%= place.place_type %></td>\n          <td><%= place.sort_order %></td>\n          <td class=\"text-right\">\n            <%= link_to 'Edit Place', edit_workflow_place_path(@workflow, place), class: 'btn btn-sm btn-link mr-2' %>\n            <%= link_to 'Delete Place', [@workflow, place], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %>\n          </td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n<div class=\"card card-body\">\n  <h2>Transitions</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Description</th>\n        <th scope=\"col\">Trigger Limit</th>\n        <th scope=\"col\">Trigger Type</th>\n        <th scope=\"col\">Sort Order</th>\n        <th scope=\"col\">Custom Form</th>\n        <th scope=\"col\">Sub Workflow</th>\n        <th scope='col'> </th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workflow.transitions.each do |transition| %>\n        <tr>\n          <td><%= transition.id %></td>\n          <td><%= link_to transition.name, workflow_transition_path(@workflow, transition) %></td>\n          <td><%= transition.description %></td>\n          <td><%= transition.trigger_limit %></td>\n          <td><%= transition.trigger_type %></td>\n          <td><%= transition.sort_order %></td>\n\n          <td>\n            <% if transition.form %>\n             <%= link_to transition.form.name, form_path(transition.form) %>\n            <% else %>\n             No Form\n            <% end %>\n          </td>\n          <td>\n            <% if transition.sub_workflow %>\n              <%= link_to transition.sub_workflow.name, workflow_path(transition.sub_workflow_id) %>\n            <% else %>\n              No\n            <% end %>\n          </td> \n          <td><%= link_to 'Edit Transition', edit_workflow_transition_path(@workflow, transition), class: 'btn btn-sm btn-link mr-2' %>\n            <%= link_to 'Delete Transition', [@workflow, transition], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n<div class=\"card card-body\">\n  <h2>Arcs</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Direction</th>\n        <th scope=\"col\">Place</th>\n        <th scope=\"col\">Transition</th>\n        <th scope=\"col\">Arc Type</th>\n        <th scope=\"col\">Guards</th>\n        <th scope='col'> </th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workflow.arcs.includes(:transition, :place).each do |arc| %>\n        <tr>\n          <td><%= link_to arc.id, workflow_arc_path(@workflow, arc) %></td>\n          <td><%= link_to arc.name, workflow_arc_path(@workflow, arc) %></td>\n          <td><%= arc.direction %></td>\n          <td><%= arc.place&.name %></td>\n          <td><%= arc.transition&.name %></td>\n          <td><code><%= arc.guards.map(&:inspect).join(\" & \") %></code></td>\n          <td>\n            <% if arc.out? %>\n              <%= link_to 'Add Gruards', new_arc_guard_path(arc), class: 'btn btn-sm btn-outline-primary mr-2' %>\n            <% end %>\n          </td>\n          <td class=\"text-right\">\n            <%= link_to 'Edit Arc', edit_workflow_arc_path(@workflow, arc), class: 'btn btn-sm btn-link mr-2' %>\n            <%= link_to 'Delete Arc', [@workflow, arc], remote: true, method: :delete, data: {confirm: 'confirm?'}, class: 'btn btn-sm btn-link text-danger' %>\n          </td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n"
  },
  {
    "path": "app/views/wf/workitem_assignments/new.html.erb",
    "content": "<div class=\"card card-body\">\n<%= form_with(model: @workitem_assignment, url: workitem_workitem_assignments_path(@workitem), local: true) do |f| %>\n  <% if @workitem_assignment.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(@workitem_assignment.errors.count, \"error\") %> prohibited this workitem_assignment from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% @workitem_assignment.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.label :party_id, class: \"label\" %>\n    <%= f.select :party_id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: \"form-control custom-select\", placeholder: \"party_id\" %>\n  </div>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", value: \"Assign\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>\n</div>\n"
  },
  {
    "path": "app/views/wf/workitems/index.html.erb",
    "content": "<div class=\"card card-body\">\n<div>\n  <h2 class=\"\">Stats</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">All</th>\n        <th scope=\"col\">Enabled</th>\n        <th scope=\"col\">Started</th>\n        <th scope=\"col\">Canceled</th>\n        <th scope=\"col\">Finished</th>\n        <th scope=\"col\">Overridden</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).count, workitems_path %></td>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).enabled.count, workitems_path(state: :enabled) %></td>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).started.count, workitems_path(state: :started) %></td>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).canceled.count, workitems_path(state: :canceled) %></td>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).finished.count, workitems_path(state: :finished) %></td>\n        <td><%= link_to @workitems.unscope(:offset).unscope(:limit).unscope(:order).unscope(:select).overridden.count, workitems_path(state: :overridden) %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n</div>\n<div class=\"card card-body\">\n<div>\n  <h2>Workitems</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Transition</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Holding User</th>\n        <th scope=\"col\">Started At</th>\n        <th scope=\"col\">Enabled At</th>\n        <th scope=\"col\">Canceled At</th>\n        <th scope=\"col\">Finished At</th>\n        <th scope=\"col\">Overridden At</th>\n        <th scope=\"col\">Deadline</th>\n        <th scope=\"col\">Detail</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workitems.each do |workitem| %>\n        <tr>\n          <td><%= link_to workitem.id, workitem_path(workitem) %></td>\n          <td>\n            <%= link_to workitem.transition.name, workflow_transition_path(workitem.workflow, workitem.transition) %> </td>\n          <td><%= workitem.state %></td>\n          <td><%= workitem.holding_user_id %></td>\n          <td><%= workitem.started_at %></td>\n          <td><%= workitem.enabled_at %></td>\n          <td><%= workitem.canceled_at %></td>\n          <td><%= workitem.finished_at %></td>\n          <td><%= workitem.overridden_at %></td>\n          <td><%= workitem.deadline %></td>\n          <td><%= link_to \"Run\", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <%= paginate @workitems, theme: 'twitter-bootstrap-4' %>\n</div>\n</div>\n"
  },
  {
    "path": "app/views/wf/workitems/pre_finish.html.erb",
    "content": "<%= form_with(model: @workitem, url: finish_workitem_path(@workitem), method: :put, local: true) do |f| %>\n  <% if @workitem.errors.any? %>\n    <article class=\"message is-danger\">\n      <div class=\"message-header\">\n        <p>\n          <%= pluralize(@workitem.errors.count, \"error\") %> prohibited this workitem from being saved:\n        </p>\n      </div>\n      <div class=\"message-body content\">\n        <ul>\n          <% @workitem.errors.full_messages.each do |message| %>\n            <li><%= message %></li>\n          <% end %>\n        </ul>\n      </div>\n    </article>\n  <% end %>\n  \n  <% @workitem.transition.dynamic_assignments.each do |tt| %>\n    <%= f.fields_for :dynamic_assignments do |ff| %>\n       <div class=\"form-group\">\n         <%= ff.label \"Assign #{tt.name}\", class: \"label\" %>\n         <%= ff.select tt.id, Wf::Party.all.map{|x| [x.party_name, x.id]}, {}, class: \"form-control custom-select\" %>\n       </div>\n    <% end %>\n  <% end %>\n\n  \n  <% if @workitem.transition.form %>\n    <%= f.fields_for :entry do |ff| %>\n      <% @workitem.transition.form.fields.each do |field| %>\n       <div class=\"form-group\">\n        <%= ff.label field.name, class: \"label\" %>\n        <% if field.array?  %>\n          <%= ff.select field.id, {}, {}, multiple: true, class: \"form-control select2\" %>\n        <% else %>\n          <%= ff.send(field.field_type_for_view, field.id, class: \"form-control\") %>\n        <% end %>\n        </div>\n      <% end %>\n    <% end %>\n  <% end %>\n\n  <div class=\"form-group\">\n    <%= f.submit class: \"btn btn-primary\", value: \"Done\", data: {disable_with: 'Waiting...'} %>\n  </div>\n<% end %>\n\n<script>\n  $(function() {\n    $(\".select2\").select2({\n      tags: [],\n      theme: \"bootstrap4\",\n      tokenSeparators: [',', ' ']\n    });\n  });\n</script>"
  },
  {
    "path": "app/views/wf/workitems/show.html.erb",
    "content": "<div class=\"card card-body\">\n<div class=\"float-right\">\n  <%= link_to 'Back to Case', workflow_case_path(@workitem.workflow, @workitem.case), class: 'btn btn-primary' %>\n</div>\n<div>\n  <h2>Transition</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Description</th>\n        <th scope=\"col\">Trigger Limit</th>\n        <th scope=\"col\">Trigger Type</th>\n        <th scope=\"col\">Sort Order</th>\n        <th scope=\"col\">Custom Form</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><%= @workitem.transition.id %></td>\n        <td><%= @workitem.transition.name %></td>\n        <td><%= @workitem.transition.description %></td>\n        <td><%= @workitem.transition.trigger_limit %></td>\n        <td><%= @workitem.transition.trigger_type %></td>\n        <td><%= @workitem.transition.sort_order %></td>\n\n        <td>\n          <% if @workitem.transition.form %>\n           <%= link_to @workitem.transition.form.name, form_path(@workitem.transition.form) %>\n          <% else %>\n           No Form\n          <% end %>\n        </td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Detail</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Transition</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Holding User</th>\n        <th scope=\"col\">Started At</th>\n        <th scope=\"col\">Enabled At</th>\n        <th scope=\"col\">Canceled At</th>\n        <th scope=\"col\">Finished At</th>\n        <th scope=\"col\">Overridden At</th>\n        <th scope=\"col\">Deadline</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td><%= @workitem.id %></td>\n        <td>\n          <%= link_to @workitem.transition.name, workflow_transition_path(@workitem.workflow, @workitem.transition) %> </td>\n        <td><%= @workitem.state %></td>\n        <td><%= @workitem.holding_user_id %></td>\n        <td><%= @workitem.started_at %></td>\n        <td><%= @workitem.enabled_at %></td>\n        <td><%= @workitem.canceled_at %></td>\n        <td><%= @workitem.finished_at %></td>\n        <td><%= @workitem.overridden_at %></td>\n        <td><%= @workitem.deadline %></td>\n      </tr>\n    </tbody>\n  </table>\n</div>\n\n<% if @workitem.transition.form %>\n  <div>\n    <h2>Entries</h2>\n    <table class=\"table table-view\">\n      <thead>\n        <tr>\n          <th scope=\"col\">ID</th>\n          <th scope=\"col\">User</th>\n          <th scope=\"col\">Payload</th>\n        </tr>\n      </thead>\n      <tbody>\n        <% @workitem.entries.each do |entry| %>\n          <tr>\n            <td><%= entry.id %></td>\n            <td><%= entry.user_id %></td>\n            <td>\n              <pre>\n                <%= JSON.pretty_generate(entry.payload) %>\n              </pre>\n            </td>\n          </tr>\n        <% end %>\n      </tbody>\n    </table>\n  </div>\n<% end %>\n\n<div>\n  <h2>Assignments</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Party ID</th>\n        <th scope=\"col\">Name</th>\n        <th scope=\"col\">Created At</th>\n        <th scope=\"col\">Action</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workitem.workitem_assignments.includes(:party).each do |assignment|%>\n        <tr>\n          <td><%= assignment.id %></td>\n          <td><%= assignment.party_id %></td>\n          <td>\n            <%= assignment.party.party_name %> </td>\n          <td><%= assignment.created_at %></td>\n          <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>\n        </tr>\n      <% end %>\n      <tr>\n         <td colspan=\"5\">\n          <% if @workitem.transition.user? && (@workitem.started? || @workitem.enabled?) %>\n             <% if @workitem.finished_by?(wf_current_user) %>\n               <%= link_to \"Pre Finish Workitem\", pre_finish_workitem_path(@workitem), class: 'btn btn-sm btn-dark' %>\n             <% elsif @workitem.started_by?(wf_current_user)%>\n               <%= link_to \"Start Workitem\", start_workitem_path(@workitem), method: :put, class: 'btn btn-sm btn-dark' %>\n             <% else %>\n               You can not start workitem, Please assign to youself.\n             <% end %>\n             <%= link_to \"Re Assign\", new_workitem_workitem_assignment_path(@workitem), class: 'btn btn-success btn-sm' %>\n             <%= link_to \"Assign Yourself\", new_workitem_workitem_assignment_path(@workitem, party_id: wf_current_user.party&.id), class: 'btn btn-success btn-sm' %>\n             <%= link_to \"Add Comment\", new_workitem_comment_path(@workitem), class: 'btn btn-success btn-sm' %>\n            <% end %>\n          </td>\n        </tr>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>Comments</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Body</th>\n        <th scope=\"col\">User</th>\n        <th scope=\"col\">Created At</th>\n        <th scope=\"col\">Action</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workitem.comments.order(\"id DESC\").each do |comment| %>\n        <tr>\n          <td><%= comment.id %></td>\n          <td>\n            <%= comment.body %> </td>\n          <td><%= comment.user_id %></td>\n          <td><%= comment.created_at %></td>\n          <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>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n\n<div>\n  <h2>All Workitems</h2>\n  <table class=\"table table-view\">\n    <thead>\n      <tr>\n        <th scope=\"col\">ID</th>\n        <th scope=\"col\">Transition</th>\n        <th scope=\"col\">State</th>\n        <th scope=\"col\">Trigger Type</th>\n        <th scope=\"col\">Holding User</th>\n        <th scope=\"col\">Started At</th>\n        <th scope=\"col\">Enabled At</th>\n        <th scope=\"col\">Canceled At</th>\n        <th scope=\"col\">Finished At</th>\n        <th scope=\"col\">Overridden At</th>\n        <th scope=\"col\">Deadline</th>\n        <th scope=\"col\">Action</th>\n      </tr>\n    </thead>\n    <tbody>\n      <% @workitem.case.workitems.includes(:transition).each do |workitem| %>\n        <tr>\n          <td><%= link_to workitem.id, workitem_path(workitem) %></td>\n          <td>\n            <%= workitem.transition.name %> </td>\n          <td><%= workitem.state %></td>\n          <td><%= workitem.transition.trigger_type %></td>\n          <td><%= workitem.holding_user_id %></td>\n          <td><%= workitem.started_at %></td>\n          <td><%= workitem.enabled_at %></td>\n          <td><%= workitem.canceled_at %></td>\n          <td><%= workitem.finished_at %></td>\n          <td><%= workitem.overridden_at %></td>\n          <td><%= workitem.deadline %></td>\n          <td><%= link_to \"Run\", workitem_path(workitem), class: 'btn btn-sm btn-success' %></td>\n        </tr>\n      <% end %>\n    </tbody>\n  </table>\n</div>\n</div>\n"
  },
  {
    "path": "bin/rails",
    "content": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails gems\n# installed from the root of your application.\n\nENGINE_ROOT = File.expand_path('..', __dir__)\nENGINE_PATH = File.expand_path('../lib/wf/engine', __dir__)\nAPP_PATH = File.expand_path('../test/dummy/config/application', __dir__)\n\n# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n\nrequire 'rails/all'\nrequire 'rails/engine/commands'\n"
  },
  {
    "path": "config/routes.rb",
    "content": "# frozen_string_literal: true\n\nWf::Engine.routes.draw do\n  resources :workflows do\n    resources :transitions\n    resources :places\n    resources :arcs\n    resources :cases\n  end\n\n  resources :arcs do\n    resources :guards\n  end\n\n  resources :forms do\n    resources :fields\n  end\n\n  resources :transitions do\n    resources :static_assignments\n  end\n\n  resources :workitems do\n    resources :workitem_assignments, only: %i[new create destroy]\n    resources :comments, only: %i[new create destroy]\n    member do\n      put :start\n      get :pre_finish\n      put :finish\n    end\n  end\n\n  root to: \"workitems#index\"\nend\n"
  },
  {
    "path": "db/migrate/20200130201043_init.rb",
    "content": "# frozen_string_literal: true\n\nclass Init < ActiveRecord::Migration[6.0]\n  def change\n    create_table \"wf_arcs\", force: :cascade do |t|\n      t.bigint \"workflow_id\"\n      t.bigint \"transition_id\"\n      t.bigint \"place_id\"\n      t.integer \"direction\", default: 0, comment: \"0-in, 1-out\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.integer \"guards_count\", default: 0\n    end\n\n    create_table \"wf_case_assignments\", comment: \"Manual per-case assignments of transition to parties\", force: :cascade do |t|\n      t.bigint \"case_id\"\n      t.bigint \"transition_id\"\n      t.bigint \"party_id\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index %w[case_id transition_id party_id], name: \"wf_ctp_u\", unique: true\n    end\n\n    create_table \"wf_cases\", force: :cascade do |t|\n      t.bigint \"workflow_id\"\n      t.string \"targetable_type\", comment: \"point to target type of Application.\"\n      t.string \"targetable_id\", comment: \"point to target ID of Application.\"\n      t.integer \"state\", default: 0, comment: \"0-created, 1-active, 2-suspended, 3-canceled, 4-finished\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_comments\", force: :cascade do |t|\n      t.bigint \"workitem_id\"\n      t.string \"user_id\"\n      t.text \"body\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index [\"user_id\"], name: \"index_wf_comments_on_user_id\"\n      t.index [\"workitem_id\"], name: \"index_wf_comments_on_workitem_id\"\n    end\n\n    create_table \"wf_demo_targets\", comment: \"For demo, useless.\", force: :cascade do |t|\n      t.string \"name\"\n      t.string \"description\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_field_values\", force: :cascade do |t|\n      t.bigint \"workflow_id\"\n      t.bigint \"transition_id\"\n      t.bigint \"form_id\"\n      t.bigint \"field_id\"\n      t.text \"value\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index [\"field_id\"], name: \"index_wf_field_values_on_field_id\"\n      t.index [\"form_id\"], name: \"index_wf_field_values_on_form_id\"\n      t.index [\"transition_id\"], name: \"index_wf_field_values_on_transition_id\"\n      t.index [\"workflow_id\"], name: \"index_wf_field_values_on_workflow_id\"\n    end\n\n    create_table \"wf_fields\", force: :cascade do |t|\n      t.string \"name\"\n      t.bigint \"form_id\"\n      t.integer \"position\", default: 0\n      t.integer \"field_type\", default: 0\n      t.string \"field_type_name\"\n      t.string \"default_value\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index [\"form_id\"], name: \"index_wf_fields_on_form_id\"\n    end\n\n    create_table \"wf_forms\", force: :cascade do |t|\n      t.string \"name\"\n      t.text \"description\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_groups\", comment: \"For demo\", force: :cascade do |t|\n      t.string \"name\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_guards\", force: :cascade do |t|\n      t.bigint \"arc_id\"\n      t.bigint \"workflow_id\"\n      t.string \"fieldable_type\"\n      t.string \"fieldable_id\"\n      t.string \"op\"\n      t.string \"value\"\n      t.string \"exp\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index [\"arc_id\"], name: \"index_wf_guards_on_arc_id\"\n      t.index [\"workflow_id\"], name: \"index_wf_guards_on_workflow_id\"\n    end\n\n    create_table \"wf_parties\", comment: \"for groups or roles or users or positions etc.\", force: :cascade do |t|\n      t.string \"partable_type\"\n      t.string \"partable_id\"\n      t.string \"party_name\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index %w[partable_type partable_id], name: \"index_wf_parties_on_partable_type_and_partable_id\", unique: true\n    end\n\n    create_table \"wf_places\", force: :cascade do |t|\n      t.bigint \"workflow_id\"\n      t.string \"name\"\n      t.text \"description\"\n      t.integer \"sort_order\", default: 0\n      t.integer \"place_type\", default: 0, comment: \"类型：0-start，1-normal，2-end\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_tokens\", force: :cascade do |t|\n      t.bigint \"workflow_id\"\n      t.bigint \"case_id\"\n      t.string \"targetable_type\"\n      t.string \"targetable_id\"\n      t.bigint \"place_id\"\n      t.integer \"state\", default: 0, comment: \"0-free, 1-locked, 2-canceled, 3-consumed\"\n      t.bigint \"locked_workitem_id\"\n      if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.datetime \"produced_at\", default: -> { \"current_timestamp\" }\n      elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.datetime \"produced_at\", default: -> { \"timezone('utc'::text, now())\" }\n      end\n      t.datetime \"locked_at\"\n      t.datetime \"canceled_at\"\n      t.datetime \"consumed_at\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_transition_static_assignments\", comment: \"pre assignment for transition\", force: :cascade do |t|\n      t.bigint \"party_id\"\n      t.bigint \"transition_id\"\n      t.bigint \"workflow_id\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index %w[transition_id party_id], name: \"wf_tp_u\", unique: true\n    end\n\n    create_table \"wf_transitions\", force: :cascade do |t|\n      t.string \"name\"\n      t.text \"description\"\n      t.bigint \"workflow_id\"\n      t.integer \"sort_order\", default: 0\n      t.integer \"trigger_limit\", comment: \"use with timed trigger, after x minitues, trigger exec\"\n      t.integer \"trigger_type\", default: 0, comment: \"0-user,1-automatic, 2-message,3-time\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.bigint \"form_id\"\n      t.string \"enable_callback\", default: \"Wf::Callbacks::EnableDefault\"\n      t.string \"fire_callback\", default: \"Wf::Callbacks::FireDefault\"\n      t.string \"notification_callback\", default: \"Wf::Callbacks::NotificationDefault\"\n      t.string \"time_callback\", default: \"Wf::Callbacks::TimeDefault\"\n      t.string \"deadline_callback\", default: \"Wf::Callbacks::DeadlineDefault\"\n      t.string \"hold_timeout_callback\", default: \"Wf::Callbacks::HoldTimeoutDefault\"\n      t.string \"assignment_callback\", default: \"Wf::Callbacks::AssignmentDefault\"\n      t.string \"unassignment_callback\", default: \"Wf::Callbacks::UnassignmentDefault\"\n    end\n\n    create_table \"wf_users\", comment: \"For demo\", force: :cascade do |t|\n      t.string \"name\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.bigint \"group_id\"\n    end\n\n    create_table \"wf_workflows\", force: :cascade do |t|\n      t.string \"name\"\n      t.text \"description\"\n      t.boolean \"is_valid\", default: false\n      t.string \"creator_id\"\n      t.text \"error_msg\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"wf_workitem_assignments\", force: :cascade do |t|\n      t.bigint \"party_id\"\n      t.bigint \"workitem_id\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index %w[workitem_id party_id], name: \"wf_wp_u\", unique: true\n    end\n\n    create_table \"wf_workitems\", force: :cascade do |t|\n      t.bigint \"case_id\"\n      t.bigint \"workflow_id\"\n      t.bigint \"transition_id\"\n      t.string \"targetable_type\", comment: \"point to type of Application target: Task or Issue or PullRequest or Project etc.\"\n      t.string \"targetable_id\", comment: \"point to id of Application target: task_id or issue_id or pull_request_id or project_id etc.\"\n      t.integer \"state\", default: 0, comment: \"0-enabled, 1-started, 2-canceled, 3-finished,4-overridden\"\n      if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.datetime \"enabled_at\", default: -> { \"current_timestamp\" }\n      elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.datetime \"enabled_at\", default: -> { \"timezone('utc'::text, now())\" }\n      end\n      t.datetime \"started_at\"\n      t.datetime \"canceled_at\"\n      t.datetime \"finished_at\"\n      t.datetime \"overridden_at\"\n      t.datetime \"deadline\", comment: \"\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.datetime \"trigger_time\", comment: \"set when transition_trigger=TIME & trigger_limit present\"\n      t.string \"holding_user_id\", comment: \"id of App user\"\n      if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.json \"payload\", comment: \"store user input payload for workitem.\"\n      elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.json \"payload\", default: {}, comment: \"store user input payload for workitem.\"\n      end\n\n      t.index %w[state trigger_time], name: \"index_wf_workitems_on_state_and_trigger_time\"\n    end\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200130201641_init_some_data.rb",
    "content": "# frozen_string_literal: true\n\nclass InitSomeData < ActiveRecord::Migration[6.0]\n  def change\n    5.times do |i|\n      Wf::Group.create(name: \"Group#{i}\")\n    end\n\n    10.times do |i|\n      Wf::User.create(name: \"User#{i}\", group: Wf::Group.all.sample)\n    end\n\n    10.times { |i| Wf::DemoTarget.create(name: \"target #{i}\") }\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200131200455_create_wf_entries.rb",
    "content": "# frozen_string_literal: true\n\nclass CreateWfEntries < ActiveRecord::Migration[6.0]\n  def change\n    create_table :wf_entries, comment: \"user input data for workitem with form.\" do |t|\n      t.string :user_id, index: true\n      t.bigint :workitem_id, index: true\n      if /mysql/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.json \"payload\"\n      elsif /postgre/i.match?(ActiveRecord::Base.connection.adapter_name)\n        t.json \"payload\", default: {}\n      end\n      t.timestamps\n    end\n    remove_column :wf_workitems, :payload\n    add_column :wf_field_values, :entry_id, :bigint, index: true\n    add_index :wf_entries, %i[workitem_id user_id], unique: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200201001543_add_target_field_name_for_guard.rb",
    "content": "# frozen_string_literal: true\n\nclass AddTargetFieldNameForGuard < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_guards, :target_attr_name, :string, comment: \"point to workflow targetable's attribute\"\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200212120019_remove_targetable_from_workitem.rb",
    "content": "# frozen_string_literal: true\n\nclass RemoveTargetableFromWorkitem < ActiveRecord::Migration[6.0]\n  def change\n    remove_column :wf_workitems, :targetable_id\n    remove_column :wf_workitems, :targetable_type\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200213085258_add_formable.rb",
    "content": "# frozen_string_literal: true\n\nclass AddFormable < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_transitions, :form_type, :string, default: \"Wf::Form\"\n    add_index :wf_transitions, %i[form_type form_id]\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200213125753_add_form_id_for_entry.rb",
    "content": "# frozen_string_literal: true\n\nclass AddFormIdForEntry < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_entries, :form_id, :bigint, index: true\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200213130900_remove_workflow_id_from_form_related.rb",
    "content": "# frozen_string_literal: true\n\nclass RemoveWorkflowIdFromFormRelated < ActiveRecord::Migration[6.0]\n  def change\n    remove_column :wf_field_values, :transition_id\n    remove_column :wf_field_values, :workflow_id\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200220070839_remove_unused_column.rb",
    "content": "# frozen_string_literal: true\n\nclass RemoveUnusedColumn < ActiveRecord::Migration[6.0]\n  def change\n    remove_column :wf_guards, :target_attr_name\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200220072512_add_sub_workflow.rb",
    "content": "# frozen_string_literal: true\n\nclass AddSubWorkflow < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_transitions, :sub_workflow_id, :bigint, index: true\n    add_column :wf_cases, :started_by_workitem_id, :bigint, index: true, comment: \"As a sub workflow instance, it is started by one workitem.\"\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200222150432_add_multi_instance.rb",
    "content": "# frozen_string_literal: true\n\nclass AddMultiInstance < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_transitions, :multiple_instance, :boolean, default: false, comment: \"multiple instance mode or not\"\n    add_column :wf_transitions, :finish_condition, :string, comment: \"set finish condition for parent workitem.\", default: \"Wf::MultipleInstances::AllFinish\"\n    add_column :wf_workitems, :children_count, :integer, default: 0\n    add_column :wf_workitems, :children_finished_count, :integer, default: 0\n    add_column :wf_workitems, :forked, :boolean, default: false\n    add_column :wf_workitems, :parent_id, :bigint, comment: \"parent workitem id\"\n  end\nend\n"
  },
  {
    "path": "db/migrate/20200226195134_add_dynamic_assign_by.rb",
    "content": "# frozen_string_literal: true\n\nclass AddDynamicAssignBy < ActiveRecord::Migration[6.0]\n  def change\n    add_column :wf_transitions, :dynamic_assign_by_id, :bigint, comment: \"dynamic assign by other transition\", index: true\n  end\nend\n"
  },
  {
    "path": "lib/tasks/wf_tasks.rake",
    "content": "# frozen_string_literal: true\n\ndesc \"Wf tasks\"\n\ntask wf: :environment do\n  url = \"http://service-technology.org/files/lola/lola-2.0.tar.gz\"\n  path = Rails.root.join(\"tmp\", \"lola.tar.gz\").to_s\n  puts \"Downloading, wait!\"\n  puts `wget http://service-technology.org/files/lola/lola-2.0.tar.gz -v -t0 -O #{path}` unless File.exist?(path)\n  puts `cd #{Rails.root.join(\"tmp\")} && tar -zxvf lola.tar.gz`\n  puts `cd #{Rails.root.join(\"tmp/lola-2.0\")} && ./configure`\n  puts `cd #{Rails.root.join(\"tmp/lola-2.0\")} && make`\n  puts `cd #{Rails.root.join(\"tmp/lola-2.0\")} && sudo make install`\n  puts `lola --help`\nend\n"
  },
  {
    "path": "lib/wf/engine.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  class Engine < ::Rails::Engine\n    isolate_namespace Wf\n    config.autoload_paths += %W[\n      #{config.root}/app/models/wf/concerns\n    ]\n\n    config.to_prepare do\n      require_dependency(Rails.root + \"config/initializers/wf_config.rb\")\n    rescue LoadError\n      puts(\"config/initializers/wf_config.rb not found.\")\n    end\n  end\nend\n\nrequire \"bootstrap\"\nrequire \"bootstrap4-kaminari-views\"\nrequire \"jquery-rails\"\nrequire \"kaminari\"\nrequire \"simple_command\"\nrequire \"loaf\"\nrequire \"graphviz\"\nrequire \"rgl/adjacency\"\nrequire \"rgl/dijkstra\"\nrequire \"rgl/topsort\"\nrequire \"rgl/traversal\"\nrequire \"rgl/path\"\nrequire \"active_record/connection_adapters/postgresql_adapter.rb\"\nrequire \"select2-rails\"\nrequire \"mini_racer\"\n"
  },
  {
    "path": "lib/wf/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule Wf\n  VERSION = \"0.2.5\"\nend\n"
  },
  {
    "path": "lib/wf.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"wf/engine\"\n\nmodule Wf\n  class << self\n    attr_accessor :enable_callbacks\n    attr_accessor :fire_callbacks\n    attr_accessor :assignment_callbacks\n    attr_accessor :unassignment_callbacks\n    attr_accessor :notification_callbacks\n    attr_accessor :time_callbacks\n    attr_accessor :deadline_callbacks\n    attr_accessor :hold_timeout_callbacks\n\n    attr_accessor :form_class\n    attr_accessor :entry_class\n    attr_accessor :field_class\n\n    attr_accessor :user_class\n    attr_accessor :org_classes\n\n    attr_accessor :finish_conditions\n\n    attr_accessor :use_lola\n  end\n\n  self.enable_callbacks          = [\"Wf::Callbacks::EnableDefault\"]\n  self.fire_callbacks            = [\"Wf::Callbacks::FireDefault\"]\n  self.assignment_callbacks      = [\"Wf::Callbacks::AssignmentDefault\"]\n  self.unassignment_callbacks    = [\"Wf::Callbacks::UnassignmentDefault\"]\n  self.notification_callbacks    = [\"Wf::Callbacks::NotificationDefault\"]\n  self.deadline_callbacks        = [\"Wf::Callbacks::DeadlineDefault\"]\n  self.time_callbacks            = [\"Wf::Callbacks::TimeDefault\"]\n  self.hold_timeout_callbacks    = [\"Wf::Callbacks::HoldTimeoutDefault\"]\n  self.form_class                = \"::Wf::Form\"\n  self.entry_class               = \"::Wf::Entry\"\n  self.field_class               = \"::Wf::Field\"\n  self.user_class                = \"::Wf::User\"\n  self.org_classes               = { group: \"::Wf::Group\" }\n  self.finish_conditions         = [\"Wf::MultipleInstances::AllFinish\"]\n  self.use_lola                  = false\nend\n"
  },
  {
    "path": "lola.md",
    "content": "## LoLA\n\nLoLA is a Petri nets model-checking tool. To install LoLA, download lola-2.0.tar.gz from http://home.gna.org/service-tech/lola/index.html and extract it. You will need a working C++ compiler such as GCC or Clang. On Linux and OS X, LoLA can be compiled with ./configure and make. On Windows, you might need a Unix-like environment via Cygwin.\n\nLoLA can answer reachability queries using the flag '-f' followed by 'REACHABLE' and a formula, e.g.:\n\n  lola petrinet.lola -f \"REACHABLE DEADLOCK\"             # Tests whether a dead marking can be reached\n  lola petrinet.lola -f \"REACHABLE (p = 1 AND q > 2)\"    # Tests whether a marking M such that\n                                                         #  M(p) = 1 and M(q) > 2 can be reached\n  lola petrinet.lola -f \"REACHABLE (p + q = 1 OR r > 2)\" # Tests whether a marking M such that\n                                                         #  M(p) + M(q) = 1 or M(r) > 2 can be reached\n  lola petrinet.lola -f \"REACHABLE FIREABLE(t)\"          # Tests whether it is possible to reach a marking\n                                                         #  at which t is fireable\n\nPositive reachability queries can be witnessed by a state and a path using flags -s and -p respectively.\n\nThe tool documentation can be found at http://download.gna.org/service-tech/lola/lola.pdf.\n\n## Check Workflow Net\n\n```\n## generate lola format file.\n\n# PLACE P1,P2,P3;\n\n# MARKING P1;\n\n# TRANSITION T1\n# CONSUME P1:1;\n# PRODUCE P3:1;\n\n# TRANSITION T2\n# CONSUME P3:1;\n# PRODUCE P2:1;\n\n## checking\n\n# reachability of final marking\n# lola /Users/hooopo/w/wf/test/dummy/tmp/1-1582990693.lola --formula=\"AGEF(P1 = 0 AND P3 = 0 AND P2 = 1)\" --json=/Users/hooopo/w/wf/test/dummy/tmp/1-reachability_of_final_marking.json\n\n# quasiliveness\n# lola /Users/hooopo/w/wf/test/dummy/tmp/1-1582990693.lola --formula=\"AG NOT FIREABLE (T1)\" --json=/Users/hooopo/w/wf/test/dummy/tmp/1-dead_transition_1.json\n# lola /Users/hooopo/w/wf/test/dummy/tmp/1-1582990693.lola --formula=\"AG NOT FIREABLE (T2)\" --json=/Users/hooopo/w/wf/test/dummy/tmp/1-dead_transition_2.json\n# deadlock\n# lola /Users/hooopo/w/wf/test/dummy/tmp/1-1582990693.lola --formula=\"EF (DEADLOCK AND (P2 = 0))\" --json=/Users/hooopo/w/wf/test/dummy/tmp/1-deadlock.json\n```\n\n"
  },
  {
    "path": "screenshots/.keep",
    "content": ""
  },
  {
    "path": "test/controllers/wf/arcs_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class ArcsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/cases_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class CasesControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/comments_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class CommentsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/fields_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class FieldsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/forms_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class FormsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/guards_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class GuardsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/places_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class PlacesControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/static_assignments_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class StaticAssignmentsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/transitions_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class TransitionsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/workflows_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class WorkflowsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/workitem_assignments_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class WorkitemAssignmentsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/controllers/wf/workitems_controller_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class WorkitemsControllerTest < ActionDispatch::IntegrationTest\n    include Engine.routes.url_helpers\n\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/dummy/.ruby-version",
    "content": "ruby-3.1.1\n"
  },
  {
    "path": "test/dummy/Rakefile",
    "content": "# frozen_string_literal: true\n\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire_relative \"config/application\"\n\nRails.application.load_tasks\n"
  },
  {
    "path": "test/dummy/app/assets/config/manifest.js",
    "content": "//= link_tree ../images\n//= link_directory ../stylesheets .css\n//= link wf_manifest.js\n"
  },
  {
    "path": "test/dummy/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/dummy/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS\n * files in this directory. Styles in this file should be added after the last require_* statement.\n * It is generally better to create a new file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/dummy/app/channels/application_cable/channel.rb",
    "content": "# frozen_string_literal: true\n\nmodule ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/channels/application_cable/connection.rb",
    "content": "# frozen_string_literal: true\n\nmodule ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/application_controller.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationController < ActionController::Base\n  def current_user\n    Wf::User.first\n  end\n\n  helper_method :current_user\nend\n"
  },
  {
    "path": "test/dummy/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/dummy/app/helpers/application_helper.rb",
    "content": "# frozen_string_literal: true\n\nmodule ApplicationHelper\nend\n"
  },
  {
    "path": "test/dummy/app/javascript/packs/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file. JavaScript code in this file should be added after the last require_* statement.\n//\n// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details\n// about supported directives.\n//\n//= require rails-ujs\n//= require activestorage\n//= require_tree .\n"
  },
  {
    "path": "test/dummy/app/jobs/application_job.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationJob < ActiveJob::Base\n  # Automatically retry jobs that encountered a deadlock\n  # retry_on ActiveRecord::Deadlocked\n\n  # Most jobs are safe to ignore if the underlying records are no longer available\n  # discard_on ActiveJob::DeserializationError\nend\n"
  },
  {
    "path": "test/dummy/app/mailers/application_mailer.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationMailer < ActionMailer::Base\n  default from: \"from@example.com\"\n  layout \"mailer\"\nend\n"
  },
  {
    "path": "test/dummy/app/models/application_record.rb",
    "content": "# frozen_string_literal: true\n\nclass ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "test/dummy/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/dummy/app/models/entry.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_entries\n#\n#  id          :integer          not null, primary key\n#  user_id     :string\n#  workitem_id :integer\n#  payload     :json             default(\"{}\")\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nclass Entry < ApplicationRecord\n  belongs_to :form\n  belongs_to :user, class_name: Wf.user_class.to_s\n  belongs_to :workitem, class_name: \"Wf::Workitem\"\n  has_many :field_values\n\n  after_initialize do\n    self.payload = {} if payload.blank?\n  end\n\n  def json\n    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\n  end\n\n  def update_payload!\n    update(payload: json)\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/models/field.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_fields\n#\n#  id              :integer          not null, primary key\n#  name            :string\n#  form_id         :integer\n#  position        :integer          default(\"0\")\n#  field_type      :integer          default(\"0\")\n#  field_type_name :string\n#  default_value   :string\n#  created_at      :datetime         not null\n#  updated_at      :datetime         not null\n#\n\nclass Field < ApplicationRecord\n  belongs_to :form, touch: true\n\n  enum field_type: {\n    string: 0,\n    integer: 1,\n    boolean: 2,\n    date: 3,\n    datetime: 4,\n    decimal: 5,\n    float: 6,\n    json: 7,\n    text: 8,\n\n    \"string[]\": 20,\n    \"integer[]\": 21,\n    \"date[]\": 23,\n    \"datetime[]\": 24,\n    \"decimal[]\": 25,\n    \"float[]\": 26,\n    \"json[]\": 27,\n    \"text[]\": 28\n  }\n\n  # TODO: array type\n  def field_type_for_view\n    case field_type\n    when \"string\"\n      \"text_field\"\n    when \"integer\"\n      \"number_field\"\n    when \"date\"\n      \"date_field\"\n    when \"datetime\"\n      \"datetime_field\"\n    when \"boolean\"\n      \"check_box\"\n    when \"text\"\n      \"text_area\"\n    else\n      \"text_field\"\n    end\n  end\n\n  def array?\n    field_type.to_s.match(/^(\\w+)(\\[\\])?$/)[2] == \"[]\"\n  end\n\n  delegate :cast, to: :type_for_cast\n\n  def type_for_cast\n    type = field_type.to_s.match(/^(\\w+)(\\[\\])?$/)[1]\n    if array?\n      ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql, array: true)\n    else\n      ActiveRecord::Type.lookup(type.to_sym, adapter: :postgresql)\n    end\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/models/field_value.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_field_values\n#\n#  id            :integer          not null, primary key\n#  workflow_id   :integer\n#  transition_id :integer\n#  form_id       :integer\n#  field_id      :integer\n#  value         :text\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#  entry_id      :integer\n#\n\nclass FieldValue < ApplicationRecord\n  belongs_to :form\n  belongs_to :field\n  belongs_to :entry\n\n  def value_after_cast\n    ov = self[:value]\n    if field.array? && !ov.is_a?(Array)\n      v = begin\n            JSON.parse(ov)\n          rescue StandardError\n            []\n          end\n      field.type_for_cast.cast(v)\n    else\n      field.type_for_cast.cast(ov)\n    end\n  end\n\n  def value=(v)\n    self[:value] = if field.array?\n      Array(v.as_json)\n    else\n      v\n    end\n  end\n\n  def value\n    value_after_cast\n  end\nend\n"
  },
  {
    "path": "test/dummy/app/models/form.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_forms\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nclass Form < ApplicationRecord\n  has_many :fields, dependent: :destroy\n  has_many :entries\nend\n"
  },
  {
    "path": "test/dummy/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Dummy</title>\n    <%= csrf_meta_tags %>\n    <%= csp_meta_tag %>\n\n    <%= stylesheet_link_tag    'application', media: 'all' %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/dummy/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/dummy/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/dummy/bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path('../config/application', __dir__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/dummy/bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/dummy/bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire 'fileutils'\n\n# path to your application root.\nAPP_ROOT = File.expand_path('..', __dir__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nFileUtils.chdir APP_ROOT do\n  # This script is a way to setup or update your development environment automatically.\n  # This script is idempotent, so that you can run it at anytime and get an expectable outcome.\n  # Add necessary setup steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system('bundle check') || system!('bundle install')\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?('config/database.yml')\n  #   FileUtils.cp 'config/database.yml.sample', 'config/database.yml'\n  # end\n\n  puts \"\\n== Preparing database ==\"\n  system! 'bin/rails db:prepare'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "test/dummy/config/application.rb",
    "content": "# frozen_string_literal: true\n\nrequire_relative \"boot\"\n\nrequire \"rails/all\"\n\nBundler.require(*Rails.groups)\nrequire \"wf\"\n\nmodule Dummy\n  class Application < Rails::Application\n    # Initialize configuration defaults for originally generated Rails version.\n    config.load_defaults 6.0\n\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration can go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded after loading\n    # the framework and any gems in your application.\n  end\nend\n"
  },
  {
    "path": "test/dummy/config/boot.rb",
    "content": "# frozen_string_literal: true\n\n# Set up gems listed in the Gemfile.\nENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../../../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" if File.exist?(ENV[\"BUNDLE_GEMFILE\"])\n$LOAD_PATH.unshift File.expand_path(\"../../../lib\", __dir__)\n"
  },
  {
    "path": "test/dummy/config/cable.yml",
    "content": "development:\n  adapter: async\n\ntest:\n  adapter: test\n\nproduction:\n  adapter: redis\n  url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n  channel_prefix: dummy_production\n"
  },
  {
    "path": "test/dummy/config/database.yml",
    "content": "# PostgreSQL. Versions 9.3 and up are supported.\n#\n# Install the pg driver:\n#   gem install pg\n# On macOS with Homebrew:\n#   gem install pg -- --with-pg-config=/usr/local/bin/pg_config\n# On macOS with MacPorts:\n#   gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config\n# On Windows:\n#   gem install pg\n#       Choose the win32 build.\n#       Install PostgreSQL and put its /bin directory on your path.\n#\n# Configure Using Gemfile\n# gem 'pg'\n#\ndefault: &default\n  adapter: postgresql\n  encoding: unicode\n  # For details on connection pooling, see Rails configuration guide\n  # https://guides.rubyonrails.org/configuring.html#database-pooling\n  pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>\n\ndevelopment:\n  <<: *default\n  database: wf_dummy_development\n\n  # The specified database role being used to connect to postgres.\n  # To create additional roles in postgres see `$ createuser --help`.\n  # When left blank, postgres will use the default role. This is\n  # the same name as the operating system user that initialized the database.\n  #username: dummy\n\n  # The password associated with the postgres role (username).\n  #password:\n\n  # Connect on a TCP socket. Omitted by default since the client uses a\n  # domain socket that doesn't need configuration. Windows does not have\n  # domain sockets, so uncomment these lines.\n  #host: localhost\n\n  # The TCP port the server listens on. Defaults to 5432.\n  # If your server runs on a different port number, change accordingly.\n  #port: 5432\n\n  # Schema search path. The server defaults to $user,public\n  #schema_search_path: myapp,sharedapp,public\n\n  # Minimum log levels, in increasing order:\n  #   debug5, debug4, debug3, debug2, debug1,\n  #   log, notice, warning, error, fatal, and panic\n  # Defaults to warning.\n  #min_messages: notice\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: dummy_test\n\n# As with config/credentials.yml, you never want to store sensitive information,\n# like your database password, in your source code. If your source code is\n# ever seen by anyone, they now have access to your database.\n#\n# Instead, provide the password as a unix environment variable when you boot\n# the app. Read https://guides.rubyonrails.org/configuring.html#configuring-a-database\n# for a full rundown on how to provide these environment variables in a\n# production deployment.\n#\n# On Heroku and other platform providers, you may have a full connection URL\n# available as an environment variable. For example:\n#\n#   DATABASE_URL=\"postgres://myuser:mypass@localhost/somedatabase\"\n#\n# You can use this database configuration with:\n#\n#   production:\n#     url: <%= ENV['DATABASE_URL'] %>\n#\nproduction:\n  <<: *default\n  database: dummy_production\n  username: dummy\n  password: <%= ENV['DUMMY_DATABASE_PASSWORD'] %>\n"
  },
  {
    "path": "test/dummy/config/environment.rb",
    "content": "# frozen_string_literal: true\n\n# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/dummy/config/environments/development.rb",
    "content": "# frozen_string_literal: true\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join(\"tmp\", \"caching-dev.txt\").exist?\n    config.action_controller.perform_caching = true\n    config.action_controller.enable_fragment_cache_logging = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      \"Cache-Control\" => \"public, max-age=#{2.days.to_i}\"\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Highlight code that triggered database queries in logs.\n  config.active_record.verbose_query_logs = true\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug = true\n\n  # Suppress logger output for asset requests.\n  config.assets.quiet = true\n\n  # Raises error for missing translations.\n  # config.action_view.raise_on_missing_translations = true\n\n  # Use an evented file watcher to asynchronously detect changes in source code,\n  # routes, locales, etc. This feature depends on the listen gem.\n  # config.file_watcher = ActiveSupport::EventedFileUpdateChecker\nend\n"
  },
  {
    "path": "test/dummy/config/environments/production.rb",
    "content": "# frozen_string_literal: true\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in either ENV[\"RAILS_MASTER_KEY\"]\n  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV[\"RAILS_SERVE_STATIC_FILES\"].present?\n\n  # Compress CSS using a preprocessor.\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = 'http://assets.example.com'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain.\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = 'wss://example.com/cable'\n  # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Use the lowest log level to ensure availability of diagnostic information\n  # when problems arise.\n  config.log_level = :debug\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [:request_id]\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment).\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"dummy_production\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Use a different logger for distributed setups.\n  # require 'syslog/logger'\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')\n\n  if ENV[\"RAILS_LOG_TO_STDOUT\"].present?\n    logger           = ActiveSupport::Logger.new(STDOUT)\n    logger.formatter = config.log_formatter\n    config.logger    = ActiveSupport::TaggedLogging.new(logger)\n  end\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n\n  # Inserts middleware to perform automatic connection switching.\n  # The `database_selector` hash is used to pass options to the DatabaseSelector\n  # middleware. The `delay` is used to determine how long to wait after a write\n  # to send a subsequent read to the primary.\n  #\n  # The `database_resolver` class is used by the middleware to determine which\n  # database is appropriate to use based on the time delay.\n  #\n  # The `database_resolver_context` class is used by the middleware to set\n  # timestamps for the last write to the primary. The resolver uses the context\n  # class timestamps to determine how long to wait before reading from the\n  # replica.\n  #\n  # By default Rails will store a last write timestamp in the session. The\n  # DatabaseSelector middleware is designed as such you can define your own\n  # strategy for connection switching and pass that into the middleware through\n  # these configuration options.\n  # config.active_record.database_selector = { delay: 2.seconds }\n  # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver\n  # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session\nend\n"
  },
  {
    "path": "test/dummy/config/environments/test.rb",
    "content": "# frozen_string_literal: true\n\n# The test environment is used exclusively to run your application's\n# test suite. You never need to work with it otherwise. Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs. Don't rely on the data there!\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  config.cache_classes = false\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    \"Cache-Control\" => \"public, max-age=#{1.hour.to_i}\"\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n  config.cache_store = :null_store\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory.\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations.\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/dummy/config/initializers/application_controller_renderer.rb",
    "content": "# frozen_string_literal: true\n# Be sure to restart your server when you modify this file.\n\n# ActiveSupport::Reloader.to_prepare do\n#   ApplicationController.renderer.defaults.merge!(\n#     http_host: 'example.org',\n#     https: false\n#   )\n# end\n"
  },
  {
    "path": "test/dummy/config/initializers/assets.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = \"1.0\"\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in the app/assets\n# folder are already added.\n# Rails.application.config.assets.precompile += %w( admin.js admin.css )\n"
  },
  {
    "path": "test/dummy/config/initializers/backtrace_silencers.rb",
    "content": "# frozen_string_literal: true\n# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/dummy/config/initializers/content_security_policy.rb",
    "content": "# frozen_string_literal: true\n# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy\n# For further information see the following documentation\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy\n\n# Rails.application.config.content_security_policy do |policy|\n#   policy.default_src :self, :https\n#   policy.font_src    :self, :https, :data\n#   policy.img_src     :self, :https, :data\n#   policy.object_src  :none\n#   policy.script_src  :self, :https\n#   policy.style_src   :self, :https\n\n#   # Specify URI for violation reports\n#   # policy.report_uri \"/csp-violation-report-endpoint\"\n# end\n\n# If you are using UJS then enable automatic nonce generation\n# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }\n\n# Set the nonce only to specific directives\n# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)\n\n# Report CSP violations to a specified URI\n# For further information see the following documentation:\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only\n# Rails.application.config.content_security_policy_report_only = true\n"
  },
  {
    "path": "test/dummy/config/initializers/cookies_serializer.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# Specify a serializer for the signed and encrypted cookie jars.\n# Valid options are :json, :marshal, and :hybrid.\nRails.application.config.action_dispatch.cookies_serializer = :json\n"
  },
  {
    "path": "test/dummy/config/initializers/filter_parameter_logging.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/dummy/config/initializers/inflections.rb",
    "content": "# frozen_string_literal: true\n# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/dummy/config/initializers/mime_types.rb",
    "content": "# frozen_string_literal: true\n# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "test/dummy/config/initializers/my_assignment_callback.rb",
    "content": "# frozen_string_literal: true\n\nclass MyAssignmentCallback\n  def perform(_workitem_id)\n    Wf::Party.all.sample(2)\n  end\nend\n\nWf.assignment_callbacks = [\"Wf::Callbacks::AssignmentDefault\", \"MyAssignmentCallback\"]\n"
  },
  {
    "path": "test/dummy/config/initializers/wf_config.rb",
    "content": "# frozen_string_literal: true\n\n# TODO: use setter\nWf.user_class  = \"::Wf::User\"\nWf.org_classes = { group: \"::Wf::Group\" }\nWf.form_class  = \"Wf::Form\"\nWf.entry_class = \"Wf::Entry\"\nWf.field_class = \"Wf::Field\"\n"
  },
  {
    "path": "test/dummy/config/initializers/wrap_parameters.rb",
    "content": "# frozen_string_literal: true\n\n# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#   self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/dummy/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# The following keys must be escaped otherwise they will not be retrieved by\n# the default I18n backend:\n#\n# true, false, on, off, yes, no\n#\n# Instead, surround them with single quotes.\n#\n# en:\n#   'true': 'foo'\n#\n# To learn more, please read the Rails Internationalization guide\n# available at https://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/dummy/config/mysql_database.yml",
    "content": "development:\n  prepared_statements: false\n  encoding: utf8mb4\n  socket: /tmp/mysql.sock\n  adapter: mysql2\n  database: petri_flow_dev\n  username: root\n  min_messages: warning\n  pool: 5\n  timeout: 5000\n  checkout_timeout: <%= ENV['CHECKOUT_TIMEOUT'] || 5 %>\n\ntest:\n  prepared_statements: false\n  encoding: utf8mb4\n  socket: /tmp/mysql.sock\n  adapter: mysql2\n  database: petri_flow_test\n  username: root\n  min_messages: warning\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/dummy/config/puma.rb",
    "content": "# frozen_string_literal: true\n\n# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers: a minimum and maximum.\n# Any libraries that use thread pools should be configured to match\n# the maximum value specified for Puma. Default is set to 5 threads for minimum\n# and maximum; this matches the default thread size of Active Record.\n#\nmax_threads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }\nmin_threads_count = ENV.fetch(\"RAILS_MIN_THREADS\") { max_threads_count }\nthreads min_threads_count, max_threads_count\n\n# Specifies the `port` that Puma will listen on to receive requests; default is 3000.\n#\nport        ENV.fetch(\"PORT\") { 3000 }\n\n# Specifies the `environment` that Puma will run in.\n#\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\n# Specifies the `pidfile` that Puma will use.\npidfile ENV.fetch(\"PIDFILE\") { \"tmp/pids/server.pid\" }\n\n# Specifies the number of `workers` to boot in clustered mode.\n# Workers are forked web server processes. If using threads and workers together\n# the concurrency of the application would be max `threads` * `workers`.\n# Workers do not work on JRuby or Windows (both of which do not support\n# processes).\n#\n# workers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\n\n# Use the `preload_app!` method when specifying a `workers` number.\n# This directive tells Puma to first boot the application and load code\n# before forking the application. This takes advantage of Copy On Write\n# process behavior so workers use less memory.\n#\n# preload_app!\n\n# Allow puma to be restarted by `rails restart` command.\nplugin :tmp_restart\n"
  },
  {
    "path": "test/dummy/config/routes.rb",
    "content": "# frozen_string_literal: true\n\nRails.application.routes.draw do\n  mount Wf::Engine => \"/wf\"\nend\n"
  },
  {
    "path": "test/dummy/config/spring.rb",
    "content": "# frozen_string_literal: true\n\nSpring.watch(\n  \".ruby-version\",\n  \".rbenv-vars\",\n  \"tmp/restart.txt\",\n  \"tmp/caching-dev.txt\"\n)\n"
  },
  {
    "path": "test/dummy/config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   credentials: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket\n\n# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "test/dummy/config.ru",
    "content": "# frozen_string_literal: true\n\n# This file is used by Rack-based servers to start the application.\n\nrequire_relative \"config/environment\"\n\nrun Rails.application\n"
  },
  {
    "path": "test/dummy/db/migrate/20200213081814_new_form.rb",
    "content": "# frozen_string_literal: true\n\nclass NewForm < ActiveRecord::Migration[6.0]\n  def change\n    create_table \"field_values\", force: :cascade do |t|\n      t.bigint \"form_id\"\n      t.bigint \"field_id\"\n      t.text \"value\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table \"fields\", force: :cascade do |t|\n      t.string \"name\"\n      t.bigint \"form_id\"\n      t.integer \"position\", default: 0\n      t.integer \"field_type\", default: 0\n      t.string \"field_type_name\"\n      t.string \"default_value\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n      t.index [\"form_id\"], name: \"index_fields_on_form_id\"\n    end\n\n    create_table \"forms\", force: :cascade do |t|\n      t.string \"name\"\n      t.text \"description\"\n      t.datetime \"created_at\", precision: 6, null: false\n      t.datetime \"updated_at\", precision: 6, null: false\n    end\n\n    create_table :entries do |t|\n      t.string :user_id, index: true\n      t.bigint :workitem_id, index: true\n      t.json \"payload\"\n      t.timestamps\n    end\n    add_index :entries, %i[workitem_id user_id], unique: true\n  end\nend\n"
  },
  {
    "path": "test/dummy/db/migrate/20200213133942_add_form_id_in_entry1.rb",
    "content": "# frozen_string_literal: true\n\nclass AddFormIdInEntry1 < ActiveRecord::Migration[6.0]\n  def change\n    add_column :entries, :form_id, :bigint, index: true\n  end\nend\n"
  },
  {
    "path": "test/dummy/db/migrate/20200214005535_add_entry_id_for_field_values1.rb",
    "content": "# frozen_string_literal: true\n\nclass AddEntryIdForFieldValues1 < ActiveRecord::Migration[6.0]\n  def change\n    add_column :field_values, :entry_id, :bigint, index: true\n  end\nend\n"
  },
  {
    "path": "test/dummy/db/schema.rb",
    "content": "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# This file is the source Rails uses to define your schema when running `rails\n# db:schema:load`. When creating a new database, `rails db:schema:load` tends to\n# be faster and is potentially less error prone than running all of your\n# migrations from scratch. Old migrations may fail to apply correctly if those\n# migrations use external dependencies or application code.\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 2020_02_26_195134) do\n\n  # These are extensions that must be enabled in order to support this database\n  enable_extension \"plpgsql\"\n\n  create_table \"entries\", force: :cascade do |t|\n    t.string \"user_id\"\n    t.bigint \"workitem_id\"\n    t.json \"payload\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"form_id\"\n    t.index [\"user_id\"], name: \"index_entries_on_user_id\"\n    t.index [\"workitem_id\", \"user_id\"], name: \"index_entries_on_workitem_id_and_user_id\", unique: true\n    t.index [\"workitem_id\"], name: \"index_entries_on_workitem_id\"\n  end\n\n  create_table \"field_values\", force: :cascade do |t|\n    t.bigint \"form_id\"\n    t.bigint \"field_id\"\n    t.text \"value\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"entry_id\"\n  end\n\n  create_table \"fields\", force: :cascade do |t|\n    t.string \"name\"\n    t.bigint \"form_id\"\n    t.integer \"position\", default: 0\n    t.integer \"field_type\", default: 0\n    t.string \"field_type_name\"\n    t.string \"default_value\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"form_id\"], name: \"index_fields_on_form_id\"\n  end\n\n  create_table \"forms\", force: :cascade do |t|\n    t.string \"name\"\n    t.text \"description\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_arcs\", force: :cascade do |t|\n    t.bigint \"workflow_id\"\n    t.bigint \"transition_id\"\n    t.bigint \"place_id\"\n    t.integer \"direction\", default: 0, comment: \"0-in, 1-out\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.integer \"guards_count\", default: 0\n  end\n\n  create_table \"wf_case_assignments\", comment: \"Manual per-case assignments of transition to parties\", force: :cascade do |t|\n    t.bigint \"case_id\"\n    t.bigint \"transition_id\"\n    t.bigint \"party_id\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"case_id\", \"transition_id\", \"party_id\"], name: \"wf_ctp_u\", unique: true\n  end\n\n  create_table \"wf_cases\", force: :cascade do |t|\n    t.bigint \"workflow_id\"\n    t.string \"targetable_type\", comment: \"point to target type of Application.\"\n    t.string \"targetable_id\", comment: \"point to target ID of Application.\"\n    t.integer \"state\", default: 0, comment: \"0-created, 1-active, 2-suspended, 3-canceled, 4-finished\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"started_by_workitem_id\", comment: \"As a sub workflow instance, it is started by one workitem.\"\n  end\n\n  create_table \"wf_comments\", force: :cascade do |t|\n    t.bigint \"workitem_id\"\n    t.string \"user_id\"\n    t.text \"body\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"user_id\"], name: \"index_wf_comments_on_user_id\"\n    t.index [\"workitem_id\"], name: \"index_wf_comments_on_workitem_id\"\n  end\n\n  create_table \"wf_demo_targets\", comment: \"For demo, useless.\", force: :cascade do |t|\n    t.string \"name\"\n    t.string \"description\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_entries\", comment: \"user input data for workitem with form.\", force: :cascade do |t|\n    t.string \"user_id\"\n    t.bigint \"workitem_id\"\n    t.json \"payload\", default: {}\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"form_id\"\n    t.index [\"user_id\"], name: \"index_wf_entries_on_user_id\"\n    t.index [\"workitem_id\", \"user_id\"], name: \"index_wf_entries_on_workitem_id_and_user_id\", unique: true\n    t.index [\"workitem_id\"], name: \"index_wf_entries_on_workitem_id\"\n  end\n\n  create_table \"wf_field_values\", force: :cascade do |t|\n    t.bigint \"form_id\"\n    t.bigint \"field_id\"\n    t.text \"value\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"entry_id\"\n    t.index [\"field_id\"], name: \"index_wf_field_values_on_field_id\"\n    t.index [\"form_id\"], name: \"index_wf_field_values_on_form_id\"\n  end\n\n  create_table \"wf_fields\", force: :cascade do |t|\n    t.string \"name\"\n    t.bigint \"form_id\"\n    t.integer \"position\", default: 0\n    t.integer \"field_type\", default: 0\n    t.string \"field_type_name\"\n    t.string \"default_value\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"form_id\"], name: \"index_wf_fields_on_form_id\"\n  end\n\n  create_table \"wf_forms\", force: :cascade do |t|\n    t.string \"name\"\n    t.text \"description\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_groups\", comment: \"For demo\", force: :cascade do |t|\n    t.string \"name\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_guards\", force: :cascade do |t|\n    t.bigint \"arc_id\"\n    t.bigint \"workflow_id\"\n    t.string \"fieldable_type\"\n    t.string \"fieldable_id\"\n    t.string \"op\"\n    t.string \"value\"\n    t.string \"exp\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"arc_id\"], name: \"index_wf_guards_on_arc_id\"\n    t.index [\"workflow_id\"], name: \"index_wf_guards_on_workflow_id\"\n  end\n\n  create_table \"wf_parties\", comment: \"for groups or roles or users or positions etc.\", force: :cascade do |t|\n    t.string \"partable_type\"\n    t.string \"partable_id\"\n    t.string \"party_name\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"partable_type\", \"partable_id\"], name: \"index_wf_parties_on_partable_type_and_partable_id\", unique: true\n  end\n\n  create_table \"wf_places\", force: :cascade do |t|\n    t.bigint \"workflow_id\"\n    t.string \"name\"\n    t.text \"description\"\n    t.integer \"sort_order\", default: 0\n    t.integer \"place_type\", default: 0, comment: \"类型：0-start，1-normal，2-end\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_tokens\", force: :cascade do |t|\n    t.bigint \"workflow_id\"\n    t.bigint \"case_id\"\n    t.string \"targetable_type\"\n    t.string \"targetable_id\"\n    t.bigint \"place_id\"\n    t.integer \"state\", default: 0, comment: \"0-free, 1-locked, 2-canceled, 3-consumed\"\n    t.bigint \"locked_workitem_id\"\n    t.datetime \"produced_at\", default: -> { \"timezone('utc'::text, now())\" }\n    t.datetime \"locked_at\"\n    t.datetime \"canceled_at\"\n    t.datetime \"consumed_at\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_transition_static_assignments\", comment: \"pre assignment for transition\", force: :cascade do |t|\n    t.bigint \"party_id\"\n    t.bigint \"transition_id\"\n    t.bigint \"workflow_id\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"transition_id\", \"party_id\"], name: \"wf_tp_u\", unique: true\n  end\n\n  create_table \"wf_transitions\", force: :cascade do |t|\n    t.string \"name\"\n    t.text \"description\"\n    t.bigint \"workflow_id\"\n    t.integer \"sort_order\", default: 0\n    t.integer \"trigger_limit\", comment: \"use with timed trigger, after x minitues, trigger exec\"\n    t.integer \"trigger_type\", default: 0, comment: \"0-user,1-automatic, 2-message,3-time\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"form_id\"\n    t.string \"enable_callback\", default: \"Wf::Callbacks::EnableDefault\"\n    t.string \"fire_callback\", default: \"Wf::Callbacks::FireDefault\"\n    t.string \"notification_callback\", default: \"Wf::Callbacks::NotificationDefault\"\n    t.string \"time_callback\", default: \"Wf::Callbacks::TimeDefault\"\n    t.string \"deadline_callback\", default: \"Wf::Callbacks::DeadlineDefault\"\n    t.string \"hold_timeout_callback\", default: \"Wf::Callbacks::HoldTimeoutDefault\"\n    t.string \"assignment_callback\", default: \"Wf::Callbacks::AssignmentDefault\"\n    t.string \"unassignment_callback\", default: \"Wf::Callbacks::UnassignmentDefault\"\n    t.string \"form_type\", default: \"Wf::Form\"\n    t.bigint \"sub_workflow_id\"\n    t.boolean \"multiple_instance\", default: false, comment: \"multiple instance mode or not\"\n    t.string \"finish_condition\", default: \"Wf::MultipleInstances::AllFinish\", comment: \"set finish condition for parent workitem.\"\n    t.bigint \"dynamic_assign_by_id\", comment: \"dynamic assign by other transition\"\n    t.index [\"form_type\", \"form_id\"], name: \"index_wf_transitions_on_form_type_and_form_id\"\n  end\n\n  create_table \"wf_users\", comment: \"For demo\", force: :cascade do |t|\n    t.string \"name\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.bigint \"group_id\"\n  end\n\n  create_table \"wf_workflows\", force: :cascade do |t|\n    t.string \"name\"\n    t.text \"description\"\n    t.boolean \"is_valid\", default: false\n    t.string \"creator_id\"\n    t.text \"error_msg\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n  end\n\n  create_table \"wf_workitem_assignments\", force: :cascade do |t|\n    t.bigint \"party_id\"\n    t.bigint \"workitem_id\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.index [\"workitem_id\", \"party_id\"], name: \"wf_wp_u\", unique: true\n  end\n\n  create_table \"wf_workitems\", force: :cascade do |t|\n    t.bigint \"case_id\"\n    t.bigint \"workflow_id\"\n    t.bigint \"transition_id\"\n    t.integer \"state\", default: 0, comment: \"0-enabled, 1-started, 2-canceled, 3-finished,4-overridden\"\n    t.datetime \"enabled_at\", default: -> { \"timezone('utc'::text, now())\" }\n    t.datetime \"started_at\"\n    t.datetime \"canceled_at\"\n    t.datetime \"finished_at\"\n    t.datetime \"overridden_at\"\n    t.datetime \"deadline\"\n    t.datetime \"created_at\", precision: 6, null: false\n    t.datetime \"updated_at\", precision: 6, null: false\n    t.datetime \"trigger_time\", comment: \"set when transition_trigger=TIME & trigger_limit present\"\n    t.string \"holding_user_id\", comment: \"id of App user\"\n    t.integer \"children_count\", default: 0\n    t.integer \"children_finished_count\", default: 0\n    t.boolean \"forked\", default: false\n    t.bigint \"parent_id\", comment: \"parent workitem id\"\n    t.index [\"state\", \"trigger_time\"], name: \"index_wf_workitems_on_state_and_trigger_time\"\n  end\n\nend\n"
  },
  {
    "path": "test/dummy/db/seeds.rb",
    "content": "# frozen_string_literal: true\n\nWf::Workflow.destroy_all\nWf::Case.destroy_all\nWf::Transition.destroy_all\nWf::Place.destroy_all\nWf::Arc.destroy_all\nWf::Form.destroy_all\n\nform = Wf.form_class.constantize.create(name: \"From One\")\nname_field = form.fields.create!(name: :name, field_type: :string)\nage_field = form.fields.create!(name: :age, field_type: :integer)\n\nform2 = Wf.form_class.constantize.create(name: \"From Two\")\nscore_field = form2.fields.create!(name: :score, field_type: :integer)\n\nproc do\n  seq = Wf::Workflow.create(name: \"Seq Workflow\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\")\n  t2 = seq.transitions.create!(name: \"t2\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with automatic transition\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\")\n  t2 = seq.transitions.create!(name: \"t2\", trigger_type: :automatic)\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with timed transition\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\")\n  t2 = seq.transitions.create!(name: \"t2\", trigger_type: :time, trigger_limit: 1)\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with timed split\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\")\n  t2 = seq.transitions.create!(name: \"t2\")\n  t3 = seq.transitions.create!(name: \"t3\", trigger_type: :time, trigger_limit: 1)\n\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :in, transition: t3, place: s)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc4 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc5 = seq.arcs.create!(direction: :out, transition: t2, place: e)\n  arc6 = seq.arcs.create!(direction: :out, transition: t3, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with guard\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p1 = seq.places.create!(place_type: :normal, name: \"p1\")\n  p2 = seq.places.create!(place_type: :normal, name: \"p2\")\n  t1 = seq.transitions.create!(name: \"t1\", form: form)\n  t2 = seq.transitions.create!(name: \"t2\")\n  t3 = seq.transitions.create!(name: \"t3\")\n  arc2 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p1)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p2)\n  arc4 = seq.arcs.create!(direction: :in, transition: t2, place: p1)\n  arc5 = seq.arcs.create!(direction: :in, transition: t3, place: p2)\n  arc6 = seq.arcs.create!(direction: :out, transition: t2, place: e)\n  arc7 = seq.arcs.create!(direction: :out, transition: t3, place: e)\n  arc3.guards.create!(fieldable: age_field, op: \">\".to_sym, value: 18)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with parallel routing\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p1 = seq.places.create!(place_type: :normal, name: \"p1\")\n  p2 = seq.places.create!(place_type: :normal, name: \"p2\")\n  p3 = seq.places.create!(place_type: :normal, name: \"p3\")\n  p4 = seq.places.create!(place_type: :normal, name: \"p4\")\n  t1 = seq.transitions.create!(name: \"t1\", form: form)\n  t2 = seq.transitions.create!(name: \"t2\")\n  t3 = seq.transitions.create!(name: \"t3\")\n  t4 = seq.transitions.create!(name: \"t4\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p1)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p2)\n  arc4 = seq.arcs.create!(direction: :in, transition: t2, place: p1)\n  arc5 = seq.arcs.create!(direction: :in, transition: t3, place: p2)\n  arc6 = seq.arcs.create!(direction: :out, transition: t2, place: p3)\n  arc7 = seq.arcs.create!(direction: :out, transition: t3, place: p4)\n  arc8 = seq.arcs.create!(direction: :in, transition: t4, place: p3)\n  arc9 = seq.arcs.create!(direction: :in, transition: t4, place: p4)\n  arc10 = seq.arcs.create!(direction: :out, transition: t4, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with iterative routing\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\", form: form)\n  t2 = seq.transitions.create!(name: \"t2\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\n  arc5 = seq.arcs.create!(direction: :out, transition: t1, place: s)\n  arc5.guards.create!(fieldable: age_field, op: \">\".to_sym, value: 18)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with expression guard\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p1 = seq.places.create!(place_type: :normal, name: \"p1\")\n  p2 = seq.places.create!(place_type: :normal, name: \"p2\")\n  t1 = seq.transitions.create!(name: \"t1\", form: form)\n  t2 = seq.transitions.create!(name: \"t2\")\n  t3 = seq.transitions.create!(name: \"t3\")\n  arc2 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p1)\n  arc3 = seq.arcs.create!(direction: :out, transition: t1, place: p2)\n  arc4 = seq.arcs.create!(direction: :in, transition: t2, place: p1)\n  arc5 = seq.arcs.create!(direction: :in, transition: t3, place: p2)\n  arc6 = seq.arcs.create!(direction: :out, transition: t2, place: e)\n  arc7 = seq.arcs.create!(direction: :out, transition: t3, place: e)\n  exp = <<~JS\n    let age_great_than_18 = function(){\n      if (workitem.form.age > 18) {\n        return \"Yes\"\n      } else {\n        return \"No\"\n      }\n    };\n    age_great_than_18();\n  JS\n  arc3.guards.create!(exp: exp, op: \"=\".to_sym, value: \"Yes\")\nend.call\n\nproc do\n  sub_workflow = Wf::Workflow.where(name: \"Seq Workflow\").first\n  seq = Wf::Workflow.create(name: \"Workflow with sub workflow\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\", sub_workflow: sub_workflow, trigger_type: :message)\n  t2 = seq.transitions.create!(name: \"t2\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with multiple instances\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\", trigger_type: :user, multiple_instance: true, form: form2)\n  t2 = seq.transitions.create!(name: \"t2\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\n  Wf::User.all.sample(3).each do |user|\n    t1.transition_static_assignments.create!(party: user.party)\n  end\nend.call\n\nproc do\n  seq = Wf::Workflow.create(name: \"Workflow with AssigmentCallback\")\n  s = seq.places.create!(place_type: :start, name: \"start\")\n  e = seq.places.create!(place_type: :end, name: \"end\")\n  p = seq.places.create!(place_type: :normal, name: \"p\")\n  t1 = seq.transitions.create!(name: \"t1\", assignment_callback: \"MyAssignmentCallback\")\n  t2 = seq.transitions.create!(name: \"t2\")\n  arc1 = seq.arcs.create!(direction: :in, transition: t1, place: s)\n  arc2 = seq.arcs.create!(direction: :out, transition: t1, place: p)\n  arc3 = seq.arcs.create!(direction: :in, transition: t2, place: p)\n  arc4 = seq.arcs.create!(direction: :out, transition: t2, place: e)\nend.call\n"
  },
  {
    "path": "test/dummy/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/dummy/log/.keep",
    "content": ""
  },
  {
    "path": "test/dummy/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The page you were looking for doesn't exist.</h1>\n      <p>You may have mistyped the address or the page may have moved.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The change you wanted was rejected.</h1>\n      <p>Maybe you tried to change something you didn't have access to.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>We're sorry, but something went wrong.</h1>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/dummy/storage/.keep",
    "content": ""
  },
  {
    "path": "test/fixtures/wf/case_assignments.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/comments.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/demo_targets.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/entries.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/field_values.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/fields.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/forms.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/guards.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/parties.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/transition_static_assignments.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/users.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/fixtures/wf/workitem_assignments.yml",
    "content": "# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined. If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n# column: value\n"
  },
  {
    "path": "test/integration/navigation_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nclass NavigationTest < ActionDispatch::IntegrationTest\n  # test \"the truth\" do\n  #   assert true\n  # end\nend\n"
  },
  {
    "path": "test/models/wf/case_assignment_test.rb",
    "content": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_case_assignments\n#\n#  id            :integer          not null, primary key\n#  case_id       :integer\n#  transition_id :integer\n#  party_id      :integer\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\n# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class CaseAssignmentTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/comment_test.rb",
    "content": "# frozen_string_literal: true\n# == Schema Information\n#\n# Table name: wf_comments\n#\n#  id          :integer          not null, primary key\n#  workitem_id :integer\n#  user_id     :string\n#  body        :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\n# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class CommentTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/demo_target_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_demo_targets\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :string\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class DemoTargetTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/entry_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_entries\n#\n#  id          :integer          not null, primary key\n#  user_id     :string\n#  workitem_id :integer\n#  payload     :json             default(\"{}\")\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#  form_id     :integer\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class EntryTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/field_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_fields\n#\n#  id              :integer          not null, primary key\n#  name            :string\n#  form_id         :integer\n#  position        :integer          default(\"0\")\n#  field_type      :integer          default(\"0\")\n#  field_type_name :string\n#  default_value   :string\n#  created_at      :datetime         not null\n#  updated_at      :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class FieldTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/field_value_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_field_values\n#\n#  id         :integer          not null, primary key\n#  form_id    :integer\n#  field_id   :integer\n#  value      :text\n#  created_at :datetime         not null\n#  updated_at :datetime         not null\n#  entry_id   :integer\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class FieldValueTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/form_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_forms\n#\n#  id          :integer          not null, primary key\n#  name        :string\n#  description :text\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class FormTest < ActiveSupport::TestCase\n    test \"the truth\" do\n      assert true\n    end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/guard_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_guards\n#\n#  id             :integer          not null, primary key\n#  arc_id         :integer\n#  workflow_id    :integer\n#  fieldable_type :string\n#  fieldable_id   :string\n#  op             :string\n#  value          :string\n#  exp            :string\n#  created_at     :datetime         not null\n#  updated_at     :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class GuardTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/party_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_parties\n#\n#  id            :integer          not null, primary key\n#  partable_type :string\n#  partable_id   :string\n#  party_name    :string\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class PartyTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/transition_static_assignment_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_transition_static_assignments\n#\n#  id            :integer          not null, primary key\n#  party_id      :integer\n#  transition_id :integer\n#  workflow_id   :integer\n#  created_at    :datetime         not null\n#  updated_at    :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class TransitionStaticAssignmentTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/user_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_users\n#\n#  id         :integer          not null, primary key\n#  name       :string\n#  created_at :datetime         not null\n#  updated_at :datetime         not null\n#  group_id   :integer\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class UserTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/wf_test.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"test_helper\"\n\nmodule Wf\n  class WfTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/models/wf/workitem_assignment_test.rb",
    "content": "# frozen_string_literal: true\n\n# == Schema Information\n#\n# Table name: wf_workitem_assignments\n#\n#  id          :integer          not null, primary key\n#  party_id    :integer\n#  workitem_id :integer\n#  created_at  :datetime         not null\n#  updated_at  :datetime         not null\n#\n\nrequire \"test_helper\"\n\nmodule Wf\n  class WorkitemAssignmentTest < ActiveSupport::TestCase\n    # test \"the truth\" do\n    #   assert true\n    # end\n  end\nend\n"
  },
  {
    "path": "test/test_helper.rb",
    "content": "# frozen_string_literal: true\n\n# Configure Rails Environment\nENV[\"RAILS_ENV\"] = \"test\"\n\nrequire_relative \"../test/dummy/config/environment\"\nActiveRecord::Migrator.migrations_paths = [File.expand_path(\"../test/dummy/db/migrate\", __dir__)]\nActiveRecord::Migrator.migrations_paths << File.expand_path(\"../db/migrate\", __dir__)\nrequire \"rails/test_help\"\n\n# Filter out the backtrace from minitest while preserving the one from other libraries.\nMinitest.backtrace_filter = Minitest::BacktraceFilter.new\n\n# Load fixtures from the engine\nif ActiveSupport::TestCase.respond_to?(:fixture_path=)\n  ActiveSupport::TestCase.fixture_path = File.expand_path(\"fixtures\", __dir__)\n  ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path\n  ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + \"/files\"\n  ActiveSupport::TestCase.fixtures :all\nend\n"
  },
  {
    "path": "wf.gemspec",
    "content": "# frozen_string_literal: true\n\n$LOAD_PATH.push File.expand_path(\"lib\", __dir__)\n\n# Maintain your gem's version:\nrequire \"wf/version\"\n\n# Describe your gem and declare its dependencies:\nGem::Specification.new do |spec|\n  spec.name        = \"petri_flow\"\n  spec.version     = Wf::VERSION\n  spec.authors     = [\"Hooopo Wang\"]\n  spec.email       = [\"hoooopo@gmail.com\"]\n  spec.homepage    = \"https://github.com/hooopo/petri_flow\"\n  spec.summary     = \"Petri Net Workflow Engine for Ruby.\"\n  spec.description = \"Petri Net Workflow Engine for Ruby.\"\n  spec.license     = \"MIT\"\n\n  spec.files = Dir[\"{app,config,db,lib}/**/*\", \"MIT-LICENSE\", \"Rakefile\", \"README.md\"]\n\n  spec.add_dependency \"bootstrap\", \"~> 4.4.1\"\n  spec.add_dependency \"bootstrap4-kaminari-views\"\n  spec.add_dependency \"jquery-rails\"\n  spec.add_dependency \"kaminari\"\n  spec.add_dependency \"loaf\"\n  spec.add_dependency \"mini_racer\"\n  spec.add_dependency \"pg\"\n  spec.add_dependency \"rails\"\n  spec.add_dependency \"rgl\"\n  spec.add_dependency \"ruby-graphviz\"\n  spec.add_dependency \"select2-rails-2020\"\n  spec.add_dependency \"simple_command\"\nend\n"
  }
]