├── .editorconfig ├── .gitattributes ├── .gitignore ├── .rubocop.yml ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── _assets ├── defining_form.png ├── dummy_overview.png ├── editing_transition.png ├── importing_bpmn.png └── workflow_net.png ├── _samples ├── diagram_1.bpmn ├── diagram_2.bpmn ├── diagram_3.bpmn └── diagram_4.bpmn ├── app ├── jobs │ └── workflow_core │ │ └── application_job.rb └── models │ └── workflow_core │ ├── application_record.rb │ ├── place.rb │ ├── token.rb │ ├── transition.rb │ ├── workflow.rb │ └── workflow_instance.rb ├── bin └── rails ├── db └── migrate │ ├── 20180910195950_create_workflows.rb │ ├── 20180910200203_create_workflow_transitions.rb │ ├── 20180910200454_create_workflow_places.rb │ ├── 20180912223642_create_workflow_instances.rb │ ├── 20180912223722_create_workflow_tokens.rb │ └── 20191004190723_add_start_place_id_to_workflows.rb ├── lib ├── tasks │ └── workflow_core_tasks.rake ├── workflow_core.rb └── workflow_core │ ├── concerns │ └── models │ │ ├── place.rb │ │ ├── token.rb │ │ ├── transition.rb │ │ ├── workflow.rb │ │ └── workflow_instance.rb │ ├── engine.rb │ └── version.rb ├── test ├── dummy │ ├── Rakefile │ ├── app │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ └── application.es6 │ │ │ └── stylesheets │ │ │ │ └── application.scss │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── groups_controller.rb │ │ │ ├── sessions_controller.rb │ │ │ ├── users_controller.rb │ │ │ ├── workflows │ │ │ │ ├── application_controller.rb │ │ │ │ ├── fields │ │ │ │ │ ├── application_controller.rb │ │ │ │ │ ├── options_controller.rb │ │ │ │ │ └── validations_controller.rb │ │ │ │ ├── fields_controller.rb │ │ │ │ ├── instances │ │ │ │ │ ├── application_controller.rb │ │ │ │ │ └── tokens_controller.rb │ │ │ │ ├── instances_controller.rb │ │ │ │ ├── loads_controller.rb │ │ │ │ ├── nested_forms │ │ │ │ │ ├── application_controller.rb │ │ │ │ │ └── fields_controller.rb │ │ │ │ ├── transitions │ │ │ │ │ ├── application_controller.rb │ │ │ │ │ └── options_controller.rb │ │ │ │ └── transitions_controller.rb │ │ │ └── workflows_controller.rb │ │ ├── decorators │ │ │ └── .keep │ │ ├── helpers │ │ │ ├── application_helper.rb │ │ │ ├── fields_helper.rb │ │ │ └── transitions_helper.rb │ │ ├── jobs │ │ │ └── application_job.rb │ │ ├── lib │ │ │ ├── .keep │ │ │ ├── bpmn │ │ │ │ ├── definition_container.rb │ │ │ │ ├── tokenizer.rb │ │ │ │ └── tokens │ │ │ │ │ ├── common_token.rb │ │ │ │ │ ├── end_event.rb │ │ │ │ │ ├── exclusive_gateway.rb │ │ │ │ │ ├── extensions │ │ │ │ │ └── condition_expression.rb │ │ │ │ │ ├── inclusive_gateway.rb │ │ │ │ │ ├── parallel_gateway.rb │ │ │ │ │ ├── script_task.rb │ │ │ │ │ ├── sequence_flow.rb │ │ │ │ │ ├── start_event.rb │ │ │ │ │ ├── task.rb │ │ │ │ │ ├── token.rb │ │ │ │ │ └── user_task.rb │ │ │ └── script_engine.rb │ │ ├── mailers │ │ │ └── application_mailer.rb │ │ ├── models │ │ │ ├── application_record.rb │ │ │ ├── concerns │ │ │ │ ├── .keep │ │ │ │ ├── acts_as_default_value.rb │ │ │ │ ├── enum_attribute_localizable.rb │ │ │ │ ├── fields │ │ │ │ │ └── validations │ │ │ │ │ │ ├── acceptance.rb │ │ │ │ │ │ ├── confirmation.rb │ │ │ │ │ │ ├── exclusion.rb │ │ │ │ │ │ ├── format.rb │ │ │ │ │ │ ├── inclusion.rb │ │ │ │ │ │ ├── length.rb │ │ │ │ │ │ ├── numericality.rb │ │ │ │ │ │ └── presence.rb │ │ │ │ ├── form_core │ │ │ │ │ └── acts_as_default_value.rb │ │ │ │ └── transitions │ │ │ │ │ └── options │ │ │ │ │ ├── assignable.rb │ │ │ │ │ ├── field_overridable.rb │ │ │ │ │ └── votable.rb │ │ │ ├── field.rb │ │ │ ├── field_options.rb │ │ │ ├── fields.rb │ │ │ ├── fields │ │ │ │ ├── boolean_field.rb │ │ │ │ ├── decimal_field.rb │ │ │ │ ├── integer_field.rb │ │ │ │ ├── multiple_nested_form_field.rb │ │ │ │ ├── nested_form_field.rb │ │ │ │ ├── options │ │ │ │ │ ├── decimal_field.rb │ │ │ │ │ ├── integer_field.rb │ │ │ │ │ └── text_field.rb │ │ │ │ ├── text_field.rb │ │ │ │ └── validations │ │ │ │ │ ├── boolean_field.rb │ │ │ │ │ ├── decimal_field.rb │ │ │ │ │ ├── integer_field.rb │ │ │ │ │ ├── multiple_nested_form_field.rb │ │ │ │ │ ├── nested_form_field.rb │ │ │ │ │ └── text_field.rb │ │ │ ├── form.rb │ │ │ ├── group.rb │ │ │ ├── metal_form.rb │ │ │ ├── nested_form.rb │ │ │ ├── non_configurable.rb │ │ │ ├── place.rb │ │ │ ├── places.rb │ │ │ ├── places │ │ │ │ ├── end_place.rb │ │ │ │ └── start_place.rb │ │ │ ├── token.rb │ │ │ ├── token │ │ │ │ └── payload.rb │ │ │ ├── transition.rb │ │ │ ├── transitions.rb │ │ │ ├── transitions │ │ │ │ ├── end.rb │ │ │ │ ├── exclusive_choice.rb │ │ │ │ ├── options │ │ │ │ │ ├── assigning_assignees.rb │ │ │ │ │ ├── decision.rb │ │ │ │ │ ├── exclusive_choice.rb │ │ │ │ │ └── user_task.rb │ │ │ │ ├── parallel_split.rb │ │ │ │ ├── sequence.rb │ │ │ │ ├── simple_merge.rb │ │ │ │ ├── start.rb │ │ │ │ ├── synchronization.rb │ │ │ │ └── variants │ │ │ │ │ ├── assigning_assignees.rb │ │ │ │ │ ├── decision.rb │ │ │ │ │ └── user_task.rb │ │ │ ├── user.rb │ │ │ ├── virtual_model.rb │ │ │ ├── workflow.rb │ │ │ └── workflow_instance.rb │ │ ├── presenters │ │ │ ├── application_presenter.rb │ │ │ ├── concerns │ │ │ │ └── fields │ │ │ │ │ └── presenter_for_number_field.rb │ │ │ └── fields │ │ │ │ ├── boolean_field_presenter.rb │ │ │ │ ├── composite_field_presenter.rb │ │ │ │ ├── decimal_field_presenter.rb │ │ │ │ ├── field_presenter.rb │ │ │ │ ├── integer_field_presenter.rb │ │ │ │ ├── multiple_nested_form_field_presenter.rb │ │ │ │ ├── nested_form_field_presenter.rb │ │ │ │ └── text_field_presenter.rb │ │ └── views │ │ │ ├── _form_core │ │ │ ├── field_options │ │ │ │ ├── _decimal_field.html.erb │ │ │ │ ├── _integer_field.html.erb │ │ │ │ └── _text_field.html.erb │ │ │ ├── fields │ │ │ │ ├── _boolean_field.html.erb │ │ │ │ ├── _decimal_field.html.erb │ │ │ │ ├── _integer_field.html.erb │ │ │ │ ├── _multiple_nested_form_field.html.erb │ │ │ │ ├── _nested_form.html.erb │ │ │ │ ├── _nested_form_field.html.erb │ │ │ │ └── _text_field.html.erb │ │ │ ├── preview │ │ │ │ ├── _form.html.erb │ │ │ │ └── _nested_form.html.erb │ │ │ ├── render │ │ │ │ └── _form.html.erb │ │ │ └── validations │ │ │ │ ├── _acceptance.html.erb │ │ │ │ ├── _confirmation.html.erb │ │ │ │ ├── _exclusion.html.erb │ │ │ │ ├── _format.html.erb │ │ │ │ ├── _inclusion.html.erb │ │ │ │ ├── _length.html.erb │ │ │ │ ├── _numericality.html.erb │ │ │ │ └── _presence.html.erb │ │ │ ├── _workflow_core │ │ │ └── transition_options │ │ │ │ ├── _action.html.erb │ │ │ │ ├── _assignable.html.erb │ │ │ │ ├── _assigning_assignees.html.erb │ │ │ │ ├── _condition.html.erb │ │ │ │ ├── _decision.html.erb │ │ │ │ ├── _exclusive_choice.html.erb │ │ │ │ ├── _field_overridable.html.erb │ │ │ │ └── _user_task.html.erb │ │ │ ├── groups │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ └── new.html.erb │ │ │ ├── layouts │ │ │ ├── _alert.html.erb │ │ │ ├── _footer.html.erb │ │ │ ├── _nav.html.erb │ │ │ ├── _notice.html.erb │ │ │ ├── application.html.erb │ │ │ ├── workflow_instances.html.erb │ │ │ └── workflows.html.erb │ │ │ ├── users │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ └── new.html.erb │ │ │ └── workflows │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── fields │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── new.html.erb │ │ │ ├── options │ │ │ │ ├── _form.html.erb │ │ │ │ └── edit.html.erb │ │ │ └── validations │ │ │ │ ├── _form.html.erb │ │ │ │ └── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── instances │ │ │ ├── index.html.erb │ │ │ ├── new.html.erb │ │ │ ├── show.html.erb │ │ │ └── tokens │ │ │ │ ├── index.html.erb │ │ │ │ └── show.html.erb │ │ │ ├── loads │ │ │ └── show.html.erb │ │ │ ├── nested_forms │ │ │ └── fields │ │ │ │ ├── _form.html.erb │ │ │ │ ├── edit.html.erb │ │ │ │ ├── index.html.erb │ │ │ │ └── new.html.erb │ │ │ ├── new.html.erb │ │ │ ├── show.html.erb │ │ │ └── transitions │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── new.html.erb │ │ │ └── options │ │ │ ├── _form.html.erb │ │ │ └── edit.html.erb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ ├── rake │ │ ├── setup │ │ ├── update │ │ └── yarn │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── cable.yml │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── application_controller_renderer.rb │ │ │ ├── assets.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── content_security_policy.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── form_core.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ ├── spring.rb │ │ └── storage.yml │ ├── db │ │ ├── migrate │ │ │ ├── 20180916202025_create_forms.form_core.rb │ │ │ ├── 20180916202026_create_fields.form_core.rb │ │ │ ├── 20180916202027_add_attachable_to_forms.rb │ │ │ ├── 20180916202121_add_columns_to_fields.rb │ │ │ ├── 20180916202124_add_position_to_fields.rb │ │ │ ├── 20180916215022_add_columns_to_workflows.rb │ │ │ ├── 20180917182517_create_groups.rb │ │ │ ├── 20180917184018_create_users.rb │ │ │ ├── 20180918202819_add_columns_to_transitions.rb │ │ │ ├── 20180918202827_add_columns_to_places.rb │ │ │ ├── 20180922095451_add_columns_to_workflow_instances.rb │ │ │ └── 20180922095933_add_columns_to_workflow_tokens.rb │ │ ├── schema.rb │ │ └── seeds.rb │ ├── lib │ │ ├── assets │ │ │ └── .keep │ │ └── monkey_patches │ │ │ ├── active_support.rb │ │ │ ├── active_support │ │ │ └── concern+prependable.rb │ │ │ └── big_decimal.rb │ ├── log │ │ └── .keep │ ├── mruby │ │ ├── engine.gembox │ │ └── lib │ │ │ ├── .keep │ │ │ ├── core_ext.rb │ │ │ ├── harden.rb │ │ │ ├── input.rb │ │ │ ├── io.rb │ │ │ ├── output.rb │ │ │ └── script_kernel.rb │ ├── package.json │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ └── favicon.ico │ └── storage │ │ └── .keep ├── fixtures │ └── workflow_core │ │ └── .keep ├── integration │ └── .keep ├── models │ └── workflow_core │ │ └── .keep ├── test_helper.rb └── workflow_core_test.rb └── workflow_core.gemspec /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | end_of_line = lf 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb diff=ruby 2 | *.gemspec diff=ruby 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | .bundle/ 9 | 10 | # Ignore the default SQLite database. 11 | test/dummy/db/*.sqlite3 12 | test/dummy/db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | log/*.log 16 | !test/dummy/log/.keep 17 | !test/dummy/tmp/.keep 18 | test/dummy/log/*.log 19 | test/dummy/tmp/ 20 | 21 | # Ignore uploaded files in development 22 | test/dummy/storage/* 23 | !test/dummy/storage/.keep 24 | 25 | # Ignore compiled mruby in dummy app 26 | /test/dummy/mruby/bin 27 | 28 | pkg/ 29 | .byebug_history 30 | 31 | node_modules/ 32 | test/dummy/public/packs 33 | test/dummy/node_modules/ 34 | yarn-error.log 35 | 36 | *.gem 37 | 38 | .generators 39 | .rakeTasks 40 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.7.0 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | # Declare your gem's dependencies in workflow_core.gemspec. 7 | # Bundler will treat runtime dependencies like base dependencies, and 8 | # development dependencies will be added by default to the :development group. 9 | gemspec 10 | 11 | # Declare any dependencies that are still in development here instead of in 12 | # your gemspec. These might include edge Rails or gems from your path or 13 | # Git. Remember to move these dependencies to your gemspec before releasing 14 | # your gem to rubygems.org. 15 | 16 | # To use a debugger 17 | # gem 'byebug', group: [:development, :test] 18 | 19 | gem "rails", "~> 6.0.0" 20 | gem "sqlite3", "~> 1.4" 21 | 22 | # Use Puma as the app server 23 | gem "puma" 24 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 25 | gem "listen", ">= 3.0.5", "< 3.2" 26 | gem "web-console", group: :development 27 | # Call "byebug" anywhere in the code to stop execution and get a debugger console 28 | gem "byebug", group: %i[development test] 29 | 30 | # To support ES6 31 | gem "sprockets", "~> 4.0.0" 32 | # Support ES6 33 | gem "babel-transpiler" 34 | # Use SCSS for stylesheets 35 | gem "sassc-rails" 36 | # Use Uglifier as compressor for JavaScript assets 37 | gem "uglifier", ">= 1.3.0" 38 | 39 | gem "bulma-rails" 40 | gem "jquery-rails" 41 | gem "selectize-rails" 42 | gem "turbolinks" 43 | 44 | gem "rubocop" 45 | gem "rubocop-performance" 46 | gem "rubocop-rails" 47 | 48 | gem "activeentity" 49 | gem "cocoon" 50 | gem "form_core" 51 | gem "globalid" 52 | gem "script_core" 53 | 54 | gem "graphviz" 55 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Jun Jiang 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require "bundler/setup" 5 | rescue LoadError 6 | puts "You must `gem install bundler` and `bundle install` to run rake tasks" 7 | end 8 | 9 | require "rdoc/task" 10 | 11 | RDoc::Task.new(:rdoc) do |rdoc| 12 | rdoc.rdoc_dir = "rdoc" 13 | rdoc.title = "WorkflowCore" 14 | rdoc.options << "--line-numbers" 15 | rdoc.rdoc_files.include("README.md") 16 | rdoc.rdoc_files.include("lib/**/*.rb") 17 | end 18 | 19 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 20 | load "rails/tasks/engine.rake" 21 | 22 | load "rails/tasks/statistics.rake" 23 | 24 | require "bundler/gem_tasks" 25 | 26 | require "rake/testtask" 27 | 28 | Rake::TestTask.new(:test) do |t| 29 | t.libs << "test" 30 | t.pattern = "test/**/*_test.rb" 31 | t.verbose = false 32 | end 33 | 34 | task default: :test 35 | -------------------------------------------------------------------------------- /_assets/defining_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/_assets/defining_form.png -------------------------------------------------------------------------------- /_assets/dummy_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/_assets/dummy_overview.png -------------------------------------------------------------------------------- /_assets/editing_transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/_assets/editing_transition.png -------------------------------------------------------------------------------- /_assets/importing_bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/_assets/importing_bpmn.png -------------------------------------------------------------------------------- /_assets/workflow_net.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/_assets/workflow_net.png -------------------------------------------------------------------------------- /app/jobs/workflow_core/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class ApplicationJob < ActiveJob::Base 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/models/workflow_core/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class ApplicationRecord < ActiveRecord::Base 5 | self.abstract_class = true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/workflow_core/place.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class Place < ApplicationRecord 5 | include WorkflowCore::Concerns::Models::Place 6 | 7 | self.table_name = "workflow_places" 8 | 9 | belongs_to :workflow 10 | 11 | belongs_to :input_transition, optional: true, foreign_key: "input_transition_id", 12 | class_name: "WorkflowCore::Transition" 13 | belongs_to :output_transition, optional: true, foreign_key: "output_transition_id", 14 | class_name: "WorkflowCore::Transition" 15 | 16 | has_many :tokens 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/workflow_core/token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class Token < ApplicationRecord 5 | include WorkflowCore::Concerns::Models::Token 6 | 7 | self.table_name = "workflow_tokens" 8 | 9 | belongs_to :instance, 10 | class_name: "WorkflowCore::WorkflowInstance" 11 | belongs_to :workflow 12 | 13 | belongs_to :place 14 | belongs_to :previous, optional: true, 15 | class_name: "WorkflowCore::Token" 16 | 17 | enum status: { 18 | processing: 0, 19 | completed: 1, 20 | failed: 2, 21 | unexpected: 3, 22 | terminated: 4 23 | } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/workflow_core/transition.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class Transition < ApplicationRecord 5 | include WorkflowCore::Concerns::Models::Transition 6 | 7 | self.table_name = "workflow_transitions" 8 | 9 | belongs_to :workflow 10 | 11 | has_many :input_places, 12 | dependent: :nullify, 13 | foreign_key: "output_transition_id", 14 | class_name: "WorkflowCore::Place", 15 | inverse_of: :output_transition 16 | has_many :output_places, 17 | dependent: :destroy, 18 | foreign_key: "input_transition_id", 19 | class_name: "WorkflowCore::Place", 20 | inverse_of: :input_transition 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/models/workflow_core/workflow.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class Workflow < ApplicationRecord 5 | include WorkflowCore::Concerns::Models::Workflow 6 | 7 | self.table_name = "workflows" 8 | 9 | belongs_to :start_place, class_name: "WorkflowCore::Place", dependent: :destroy, optional: true 10 | 11 | has_many :transitions, class_name: "WorkflowCore::Transition", dependent: :destroy 12 | has_many :places, class_name: "WorkflowCore::Place", dependent: :destroy 13 | 14 | has_many :instances, class_name: "WorkflowCore::WorkflowInstance", dependent: :destroy 15 | has_many :tokens, class_name: "WorkflowCore::Token", dependent: :destroy 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/workflow_core/workflow_instance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class WorkflowInstance < ApplicationRecord 5 | include WorkflowCore::Concerns::Models::WorkflowInstance 6 | 7 | self.table_name = "workflow_instances" 8 | 9 | belongs_to :workflow 10 | has_many :tokens, foreign_key: "instance_id", dependent: :destroy 11 | 12 | enum status: { 13 | processing: 0, 14 | completed: 1, 15 | failed: 2, 16 | unexpected: 3, 17 | terminated: 4, 18 | draft: 10 19 | } 20 | 21 | serialize :payload 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # This command will automatically be run when you run "rails" with Rails gems 5 | # installed from the root of your application. 6 | 7 | ENGINE_ROOT = File.expand_path("..", __dir__) 8 | ENGINE_PATH = File.expand_path("../lib/workflow_core/engine", __dir__) 9 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__) 10 | 11 | # Set up gems listed in the Gemfile. 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 13 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 14 | 15 | require "rails/all" 16 | require "rails/engine/commands" 17 | -------------------------------------------------------------------------------- /db/migrate/20180910195950_create_workflows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWorkflows < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :workflows do |t| 6 | t.string :type, null: false 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20180910200203_create_workflow_transitions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWorkflowTransitions < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :workflow_transitions do |t| 6 | t.string :type, null: false 7 | t.references :workflow, foreign_key: true 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180910200454_create_workflow_places.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWorkflowPlaces < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :workflow_places do |t| 6 | t.references :input_transition, foreign_key: { to_table: "workflow_transitions" } 7 | t.references :output_transition, foreign_key: { to_table: "workflow_transitions" } 8 | 9 | t.string :type, null: false 10 | t.references :workflow, foreign_key: true 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20180912223642_create_workflow_instances.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWorkflowInstances < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :workflow_instances do |t| 6 | t.text :payload 7 | t.integer :status, null: false, default: 0 8 | 9 | t.references :workflow, foreign_key: true 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20180912223722_create_workflow_tokens.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateWorkflowTokens < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :workflow_tokens do |t| 6 | t.integer :status, null: false, default: 0 7 | 8 | t.references :place, foreign_key: { to_table: "workflow_places" } 9 | t.references :previous, foreign_key: { to_table: "workflow_tokens" } 10 | 11 | t.references :instance, foreign_key: { to_table: "workflow_instances" } 12 | t.references :workflow, foreign_key: true 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20191004190723_add_start_place_id_to_workflows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddStartPlaceIdToWorkflows < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflows do |t| 6 | t.references :start_place, foreign_key: { to_table: "workflow_places" } 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/tasks/workflow_core_tasks.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # desc "Explaining what the task does" 4 | # task :workflow_core do 5 | # # Task goes here 6 | # end 7 | -------------------------------------------------------------------------------- /lib/workflow_core.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "workflow_core/engine" 4 | 5 | require "workflow_core/concerns/models/workflow" 6 | require "workflow_core/concerns/models/transition" 7 | require "workflow_core/concerns/models/place" 8 | require "workflow_core/concerns/models/workflow_instance" 9 | require "workflow_core/concerns/models/token" 10 | 11 | module WorkflowCore 12 | end 13 | -------------------------------------------------------------------------------- /lib/workflow_core/concerns/models/place.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore::Concerns 4 | module Models 5 | module Place 6 | extend ActiveSupport::Concern 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/workflow_core/concerns/models/token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore::Concerns 4 | module Models 5 | module Token 6 | extend ActiveSupport::Concern 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/workflow_core/concerns/models/transition.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore::Concerns 4 | module Models 5 | module Transition 6 | extend ActiveSupport::Concern 7 | 8 | def fire(token, transaction_options: { requires_new: true }, **options) 9 | transaction(**transaction_options) do 10 | on_fire(token, transaction_options, options) 11 | end 12 | rescue StandardError => e 13 | rescue_fire_error e 14 | end 15 | 16 | protected 17 | 18 | def on_fire(_token, _transaction_options, **_options) 19 | raise NotImplementedError 20 | end 21 | 22 | def rescue_fire_error(ex) 23 | raise ex 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/workflow_core/concerns/models/workflow.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore::Concerns 4 | module Models 5 | module Workflow 6 | extend ActiveSupport::Concern 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/workflow_core/concerns/models/workflow_instance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore::Concerns 4 | module Models 5 | module WorkflowInstance 6 | extend ActiveSupport::Concern 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/workflow_core/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | class Engine < ::Rails::Engine 5 | isolate_namespace WorkflowCore 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/workflow_core/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WorkflowCore 4 | VERSION = "0.0.3" 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 5 | 6 | require_relative "config/application" 7 | 8 | Rails.application.load_tasks 9 | -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.es6: -------------------------------------------------------------------------------- 1 | //= require rails-ujs 2 | //= require turbolinks 3 | //= require jquery3 4 | 5 | //= require selectize 6 | //= require cocoon 7 | 8 | // Cheat form https://github.com/jgthms/bulma/blob/master/docs/_javascript/main.js 9 | document.addEventListener('DOMContentLoaded', () => { 10 | 11 | // Dropdowns 12 | 13 | const $dropdowns = getAll('.dropdown:not(.is-hoverable)'); 14 | 15 | if ($dropdowns.length > 0) { 16 | $dropdowns.forEach($el => { 17 | $el.addEventListener('click', event => { 18 | event.stopPropagation(); 19 | $el.classList.toggle('is-active'); 20 | }); 21 | }); 22 | 23 | document.addEventListener('click', event => { 24 | closeDropdowns(); 25 | }); 26 | } 27 | 28 | function closeDropdowns() { 29 | $dropdowns.forEach($el => { 30 | $el.classList.remove('is-active'); 31 | }); 32 | } 33 | 34 | // Functions 35 | 36 | function getAll(selector) { 37 | return Array.prototype.slice.call(document.querySelectorAll(selector), 0); 38 | } 39 | 40 | // Utils 41 | 42 | function removeFromArray(array, value) { 43 | if (array.includes(value)) { 44 | const value_index = array.indexOf(value); 45 | array.splice(value_index, 1); 46 | } 47 | 48 | return array; 49 | } 50 | 51 | Array.prototype.diff = function (a) { 52 | return this.filter(function (i) { 53 | return a.indexOf(i) < 0; 54 | }); 55 | }; 56 | }); 57 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "selectize"; 2 | @import "bulma"; 3 | 4 | body { 5 | display: flex; 6 | min-height: 100vh; 7 | flex-direction: column; 8 | 9 | & nav.nav { 10 | a.brand { 11 | padding: 0.5rem 0.75em 0.5em 0; 12 | } 13 | } 14 | 15 | & .main { 16 | flex: 1; 17 | } 18 | 19 | & footer.footer { 20 | padding: 3rem 1.5rem 3rem; 21 | } 22 | } 23 | 24 | .flash { 25 | overflow: hidden; 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | line-height: 2.5; 30 | background: $info; 31 | color: $text-invert; 32 | text-align: center; 33 | } 34 | 35 | #alert { 36 | background: $danger; 37 | } 38 | 39 | .nested_form_field { 40 | .collection { 41 | .nested_form { 42 | border: 1px solid $green; 43 | padding: 0.5em; 44 | margin-left: 0.75em; 45 | margin-bottom: 0.75rem; 46 | } 47 | } 48 | } 49 | 50 | .content { 51 | .nested-content { 52 | margin-left: 0.75em; 53 | margin-bottom: 1em; 54 | } 55 | } 56 | 57 | .message-body.content { 58 | > :first-child { 59 | margin-top: 0; 60 | } 61 | } 62 | 63 | .field { 64 | flex: 1; 65 | } 66 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | helper_method :current_user 5 | 6 | private 7 | 8 | def current_user 9 | if session[:current_user_id].present? 10 | @_current_user ||= 11 | User.where(id: session[:current_user_id]).first 12 | end 13 | end 14 | 15 | def require_signed_in 16 | redirect_to users_url unless current_user 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/controllers/groups_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GroupsController < ApplicationController 4 | before_action :set_group, only: %i[show edit update destroy] 5 | 6 | # GET /groups 7 | def index 8 | # https://ruby-china.org/topics/32802 9 | @groups = Group.includes(:parent) 10 | end 11 | 12 | # GET /groups/new 13 | def new 14 | @group = Group.new 15 | end 16 | 17 | # GET /groups/1/edit 18 | def edit; end 19 | 20 | # POST /groups 21 | def create 22 | @group = Group.new(group_params) 23 | 24 | if @group.save 25 | redirect_to groups_url, notice: "Group was successfully created." 26 | else 27 | render :new 28 | end 29 | end 30 | 31 | # PATCH/PUT /groups/1 32 | def update 33 | if @group.update(group_params) 34 | redirect_to groups_url, notice: "Group was successfully updated." 35 | else 36 | render :edit 37 | end 38 | end 39 | 40 | # DELETE /groups/1 41 | def destroy 42 | @group.destroy 43 | redirect_to groups_url, notice: "Group was successfully destroyed." 44 | end 45 | 46 | private 47 | 48 | # Use callbacks to share common setup or constraints between actions. 49 | def set_group 50 | @group = Group.find(params[:id]) 51 | end 52 | 53 | # Only allow a trusted parameter "white list" through. 54 | def group_params 55 | params.require(:group).permit(:name, :parent_id) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SessionsController < ApplicationController 4 | def create 5 | session[:current_user_id] = params[:user_id] 6 | redirect_back fallback_location: root_url 7 | end 8 | 9 | def destroy 10 | session[:current_user_id] = nil 11 | redirect_to root_url 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UsersController < ApplicationController 4 | before_action :set_user, only: %i[show edit update destroy] 5 | 6 | # GET /users 7 | def index 8 | @users = User.all.includes(:group) 9 | end 10 | 11 | # GET /users/new 12 | def new 13 | @user = User.new 14 | end 15 | 16 | # GET /users/1/edit 17 | def edit; end 18 | 19 | # POST /users 20 | def create 21 | @user = User.new(user_params) 22 | 23 | if @user.save 24 | redirect_to users_url, notice: "User was successfully created." 25 | else 26 | render :new 27 | end 28 | end 29 | 30 | # PATCH/PUT /users/1 31 | def update 32 | if @user.update(user_params) 33 | redirect_to users_url, notice: "User was successfully updated." 34 | else 35 | render :edit 36 | end 37 | end 38 | 39 | # DELETE /users/1 40 | def destroy 41 | @user.destroy 42 | redirect_to users_url, notice: "User was successfully destroyed." 43 | end 44 | 45 | private 46 | 47 | # Use callbacks to share common setup or constraints between actions. 48 | def set_user 49 | @user = User.find(params[:id]) 50 | end 51 | 52 | # Only allow a trusted parameter "white list" through. 53 | def user_params 54 | params.require(:user).permit(:name, :group_id) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Workflows::ApplicationController < ApplicationController 4 | layout "workflows" 5 | 6 | before_action :set_workflow 7 | 8 | protected 9 | 10 | # Use callbacks to share common setup or constraints between actions. 11 | def set_workflow 12 | @workflow = Workflow.find(params[:workflow_id]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/fields/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Fields::ApplicationController < ApplicationController 5 | before_action :set_field 6 | 7 | protected 8 | 9 | # Use callbacks to share common setup or constraints between actions. 10 | def set_field 11 | @field = @workflow.fields.find(params[:field_id]) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/fields/options_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Fields::OptionsController < Fields::ApplicationController 5 | before_action :set_options 6 | 7 | def edit; end 8 | 9 | def update 10 | @options.assign_attributes(options_params) 11 | if @options.valid? && @field.save(validate: false) 12 | redirect_to workflow_fields_url(@workflow), notice: "Field was successfully updated." 13 | else 14 | render :edit 15 | end 16 | end 17 | 18 | private 19 | 20 | def set_options 21 | @options = @field.options 22 | end 23 | 24 | def options_params 25 | params.fetch(:options, {}).permit! 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/fields/validations_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Fields::ValidationsController < Fields::ApplicationController 5 | before_action :set_validations 6 | 7 | def edit; end 8 | 9 | def update 10 | @validations.assign_attributes(validations_params) 11 | if @validations.valid? && @field.save(validate: false) 12 | redirect_to workflow_fields_url(@workflow), notice: "Field was successfully updated." 13 | else 14 | render :edit 15 | end 16 | end 17 | 18 | private 19 | 20 | def set_validations 21 | @validations = @field.validations 22 | end 23 | 24 | def validations_params 25 | params.fetch(:validations, {}).permit! 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/fields_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Workflows::FieldsController < Workflows::ApplicationController 4 | before_action :set_form 5 | before_action :set_field, only: %i[show edit update destroy] 6 | 7 | # GET /workflows/1/fields 8 | def index 9 | @fields = @form.fields.all 10 | end 11 | 12 | # GET /workflows/fields/new 13 | def new 14 | @field = @form.fields.build 15 | end 16 | 17 | # GET /workflows/1/fields/1/edit 18 | def edit; end 19 | 20 | # POST /workflows/1/fields 21 | def create 22 | @field = @form.fields.build(field_params) 23 | 24 | if @field.save! 25 | redirect_to workflow_fields_url(@workflow), notice: "Field was successfully created." 26 | else 27 | render :new 28 | end 29 | end 30 | 31 | # PATCH/PUT /workflows/1/fields/1 32 | def update 33 | if @field.update(field_params) 34 | redirect_to workflow_fields_url(@workflow), notice: "Field was successfully updated." 35 | else 36 | render :edit 37 | end 38 | end 39 | 40 | # DELETE /workflows/1/fields/1 41 | def destroy 42 | @field.destroy 43 | redirect_to workflow_fields_url(@workflow), notice: "Field was successfully destroyed." 44 | end 45 | 46 | private 47 | 48 | # Use callbacks to share common setup or constraints between actions. 49 | def set_form 50 | @form = @workflow.form 51 | end 52 | 53 | def set_field 54 | @field = @form.fields.find(params[:id]) 55 | end 56 | 57 | # Only allow a trusted parameter "white list" through. 58 | def field_params 59 | params.fetch(:field, {}).permit(:name, :label, :hint, :type) 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/instances/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Instances::ApplicationController < ApplicationController 5 | layout "workflow_instances" 6 | 7 | before_action :require_signed_in 8 | before_action :set_instance 9 | 10 | protected 11 | 12 | # Use callbacks to share common setup or constraints between actions. 13 | def set_instance 14 | @instance = @workflow.instances.find(params[:instance_id]) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/instances/tokens_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Instances::TokensController < Instances::ApplicationController 5 | before_action :set_token, only: %i[show fire] 6 | before_action :set_form_model, only: %i[show fire] 7 | 8 | # GET /workflows/1/tokens 9 | def index 10 | @tokens = @instance.tokens.includes(place: :output_transition) 11 | end 12 | 13 | # POST /workflows/1/tokens/1/fire 14 | def show 15 | @transition_valid = @token.place.output_transition.options.valid? 16 | @form_record = @virtual_model.new(@instance.payload) 17 | end 18 | 19 | # POST /workflows/1/tokens/1/fire 20 | def fire 21 | form_params = form_record_params 22 | @token.payload.note = form_params[:_note] 23 | @token.payload.action = form_params[:_action] 24 | 25 | @form_record = @virtual_model.new(@instance.payload) 26 | @form_record.assign_attributes(form_params.except(:_note, :_action)) 27 | @transition_valid = @token.place.output_transition.options.valid? 28 | 29 | if @form_record.valid? && @transition_valid 30 | @instance.update! payload: (@instance.payload || {}).merge(@form_record.serializable_hash) 31 | @token.place.output_transition.fire(@token) 32 | 33 | redirect_to workflow_instance_tokens_url(@workflow, @instance), notice: "Token was successfully fired." 34 | else 35 | render :show 36 | end 37 | end 38 | 39 | private 40 | 41 | # Use callbacks to share common setup or constraints between actions. 42 | def set_token 43 | @token = @instance.tokens.find(params[:id] || params[:token_id]) 44 | end 45 | 46 | def set_form_model 47 | @form = @workflow.form 48 | overrides = @token.place.output_transition.options.field_overrides.map { |o| { o.name => { accessibility: o.accessibility } } }.reduce(&:merge) || {} 49 | @virtual_model = @form.to_virtual_model overrides: overrides 50 | end 51 | 52 | def form_record_params 53 | params.fetch(:form_record, {}).permit! 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/instances_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Workflows::InstancesController < Workflows::ApplicationController 4 | before_action :require_signed_in 5 | before_action :set_instance, only: %i[show] 6 | 7 | # GET /workflows/1/instances 8 | def index 9 | @instances = @workflow.instances.all 10 | end 11 | 12 | # GET /workflows/1/instances 13 | def show 14 | @form = @workflow.form 15 | @virtual_model = @form.to_virtual_model 16 | @form_record = @virtual_model.new(@instance.payload) 17 | 18 | render layout: "workflow_instances" 19 | end 20 | 21 | def new 22 | @form = @workflow.form 23 | overrides = @workflow.start_place.output_transition.options.field_overrides.map { |o| { o.name => { accessibility: o.accessibility } } }.reduce(&:merge) || {} 24 | @virtual_model = @form.to_virtual_model overrides: overrides 25 | 26 | @form_record = @virtual_model.new 27 | end 28 | 29 | # POST /workflows/1/instances 30 | def create 31 | @form = @workflow.form 32 | overrides = @workflow.start_place.output_transition.options.field_overrides.map { |o| { o.name => { accessibility: o.accessibility } } }.reduce(&:merge) || {} 33 | @virtual_model = @form.to_virtual_model overrides: overrides 34 | 35 | @form_record = @virtual_model.new form_record_params 36 | render :new unless @form_record.valid? 37 | 38 | @instance = @workflow.instances.create! type: "WorkflowInstance", creator: current_user 39 | @instance.update! payload: @form_record.serializable_hash 40 | start_token = @instance.tokens.first 41 | start_token.place.output_transition.fire(start_token) 42 | 43 | redirect_to workflow_instance_tokens_url(@workflow, @instance), notice: "instance was successfully created." 44 | end 45 | 46 | private 47 | 48 | # Use callbacks to share common setup or constraints between actions. 49 | def set_instance 50 | @instance = @workflow.instances.find(params[:id]) 51 | end 52 | 53 | def form_record_params 54 | params.fetch(:form_record, {}).permit! 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/loads_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Workflows::LoadsController < Workflows::ApplicationController 4 | # GET /workflows/1/load 5 | def show; end 6 | 7 | # PATCH/PUT /workflows/1/load 8 | def update 9 | bpmn_xml = permitted_params[:bpmn_xml].presence || permitted_params[:bpmn_file]&.read 10 | @workflow.load_from_bpmn!(bpmn_xml) 11 | 12 | redirect_to workflow_url(@workflow), notice: "Workflow definition was successfully imported." 13 | end 14 | 15 | private 16 | 17 | # Only allow a trusted parameter "white list" through. 18 | def permitted_params 19 | params.permit(:bpmn_xml, :bpmn_file) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/nested_forms/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class NestedForms::ApplicationController < ApplicationController 5 | before_action :set_nested_form 6 | 7 | protected 8 | 9 | # Use callbacks to share common setup or constraints between actions. 10 | def set_nested_form 11 | @nested_form = NestedForm.find(params[:nested_form_id]) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/nested_forms/fields_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class NestedForms::FieldsController < NestedForms::ApplicationController 5 | before_action :set_field, only: %i[show edit update destroy] 6 | 7 | def index 8 | @fields = @nested_form.fields.all 9 | end 10 | 11 | def new 12 | @field = @nested_form.fields.build 13 | end 14 | 15 | def edit; end 16 | 17 | def create 18 | @field = @nested_form.fields.build(field_params) 19 | 20 | if @field.save! 21 | redirect_to workflow_nested_form_fields_url(@workflow, @nested_form), notice: "Field was successfully created." 22 | else 23 | render :new 24 | end 25 | end 26 | 27 | def update 28 | if @field.update(field_params) 29 | redirect_to workflow_nested_form_fields_url(@workflow, @nested_form), notice: "Field was successfully updated." 30 | else 31 | render :edit 32 | end 33 | end 34 | 35 | def destroy 36 | @field.destroy 37 | redirect_to workflow_nested_form_fields_url(@workflow, @nested_form), notice: "Field was successfully destroyed." 38 | end 39 | 40 | private 41 | 42 | # Use callbacks to share common setup or constraints between actions. 43 | def set_field 44 | @field = @nested_form.fields.find(params[:id]) 45 | end 46 | 47 | # Only allow a trusted parameter "white list" through. 48 | def field_params 49 | params.fetch(:field, {}).permit(:name, :label, :hint, :accessibility, :type) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/transitions/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Transitions::ApplicationController < ApplicationController 5 | before_action :set_transition 6 | 7 | protected 8 | 9 | # Use callbacks to share common setup or constraints between actions. 10 | def set_transition 11 | @transition = @workflow.transitions.find(params[:transition_id]) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/transitions/options_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Workflows 4 | class Transitions::OptionsController < Transitions::ApplicationController 5 | before_action :set_options 6 | 7 | def edit; end 8 | 9 | def update 10 | @options.assign_attributes(options_params) 11 | if @options.valid? && @transition.save(validate: false) 12 | redirect_to workflow_transitions_url(@workflow), notice: "Transition was successfully updated." 13 | else 14 | render :edit 15 | end 16 | end 17 | 18 | private 19 | 20 | def set_options 21 | @options = @transition.options 22 | end 23 | 24 | def options_params 25 | params.fetch(:options, {}).permit! 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows/transitions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Workflows::TransitionsController < Workflows::ApplicationController 4 | before_action :set_transition, only: %i[edit update destroy] 5 | 6 | # GET /workflows/1/transitions 7 | def index 8 | @transitions = @workflow.transitions.all 9 | end 10 | 11 | # GET /workflows/transitions/new 12 | def new 13 | @transition = @workflow.transitions.build 14 | end 15 | 16 | # GET /workflows/1/transitions/1/edit 17 | def edit; end 18 | 19 | # POST /workflows/1/transitions 20 | def create 21 | @transition = @workflow.transitions.build(transition_params) 22 | 23 | if @transition.save 24 | redirect_to workflow_transitions_url(@workflow), notice: "transition was successfully created." 25 | else 26 | render :new 27 | end 28 | end 29 | 30 | # PATCH/PUT /workflows/1/transitions/1 31 | def update 32 | if @transition.update(transition_params) 33 | redirect_to workflow_transitions_url(@workflow), notice: "transition was successfully updated." 34 | else 35 | render :edit 36 | end 37 | end 38 | 39 | # DELETE /workflows/1/transitions/1 40 | def destroy 41 | @transition.destroy 42 | redirect_to workflow_transitions_url(@workflow), notice: "transition was successfully destroyed." 43 | end 44 | 45 | private 46 | 47 | # Use callbacks to share common setup or constraints between actions. 48 | def set_transition 49 | @transition = @workflow.transitions.find(params[:id]) 50 | end 51 | 52 | # Only allow a trusted parameter "white list" through. 53 | def transition_params 54 | params.fetch(:transition, {}).permit(:name, :type) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/workflows_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class WorkflowsController < ApplicationController 4 | layout "application" 5 | 6 | before_action :set_workflow, only: %i[show edit update destroy] 7 | 8 | # GET /workflows 9 | def index 10 | @workflows = Workflow.all 11 | end 12 | 13 | # GET /workflows/new 14 | def new 15 | @workflow = Workflow.new 16 | end 17 | 18 | # GET /workflows/1 19 | def show 20 | render layout: "workflows" 21 | end 22 | 23 | # GET /workflows/1/edit 24 | def edit; end 25 | 26 | # POST /workflows 27 | def create 28 | @workflow = Workflow.new(workflow_params) 29 | 30 | if @workflow.save 31 | redirect_to workflow_url(@workflow), notice: "workflow was successfully created." 32 | else 33 | render :new 34 | end 35 | end 36 | 37 | # PATCH/PUT /workflows/1 38 | def update 39 | if @workflow.update(workflow_params) 40 | redirect_to workflow_url(@workflow), notice: "workflow was successfully updated." 41 | else 42 | render :edit 43 | end 44 | end 45 | 46 | # DELETE /workflows/1 47 | def destroy 48 | @workflow.destroy 49 | redirect_to workflows_url, notice: "workflow was successfully destroyed." 50 | end 51 | 52 | private 53 | 54 | # Use callbacks to share common setup or constraints between actions. 55 | def set_workflow 56 | @workflow = Workflow.find(params[:id]) 57 | end 58 | 59 | # Only allow a trusted parameter "white list" through. 60 | def workflow_params 61 | params.fetch(:workflow, {}).permit(:name, :description) 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /test/dummy/app/decorators/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/app/decorators/.keep -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationHelper 4 | def options_for_enum_select(klass, attribute, selected = nil) 5 | container = klass.public_send(attribute.to_s.pluralize).map do |k, v| 6 | v ||= k 7 | [klass.human_enum_value(attribute, k), v] 8 | end 9 | 10 | options_for_select(container, selected) 11 | end 12 | 13 | def present(model, options = {}) 14 | klass = options.delete(:presenter_class) || "#{model.class}Presenter".constantize 15 | presenter = klass.new(model, self, options) 16 | 17 | yield(presenter) if block_given? 18 | 19 | presenter 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/fields_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module FieldsHelper 4 | def options_for_field_types(selected: nil) 5 | options_for_select(Field.descendants.map { |klass| [klass.model_name.human, klass.to_s] }, selected) 6 | end 7 | 8 | def field_label(form, field_name:) 9 | field_name = field_name.to_s.split(".").first.to_sym 10 | 11 | form.fields.select do |field| 12 | field.name == field_name 13 | end.first&.label 14 | end 15 | 16 | def smart_form_fields_path(workflow, form) 17 | case form 18 | when Form 19 | workflow_fields_path(workflow, form) 20 | when NestedForm 21 | workflow_nested_form_fields_path(workflow, form) 22 | else 23 | raise "Unknown form: #{form.class}" 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/transitions_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module TransitionsHelper 4 | def options_for_transition_types(selected: nil) 5 | options_for_select(Transition.descendants.map { |klass| [klass.model_name.human, klass.to_s] }, selected) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationJob < ActiveJob::Base 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/lib/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/app/lib/.keep -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/definition_container.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class DefinitionContainer 5 | attr_reader :collection 6 | 7 | def initialize(tokens) 8 | @collection = tokens.map { |t| { t.id => t } }.reduce(&:merge!) 9 | @start_id = tokens.first { |t| t.node_type == :start_event }.id 10 | end 11 | 12 | def start_event 13 | @collection[@start_id] 14 | end 15 | 16 | def [](id) 17 | @collection[id] 18 | end 19 | 20 | def slice(*ids) 21 | @collection.slice(*ids).values 22 | end 23 | 24 | def self.parse(bpmn_xml) 25 | tokens = Bpmn::Tokenizer.new.tokenize(bpmn_xml) 26 | return nil unless tokens 27 | 28 | new tokens 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokenizer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokenizer 5 | def tokenize(bpmn_xml) 6 | return nil if bpmn_xml.blank? 7 | 8 | doc = begin 9 | Nokogiri::XML(bpmn_xml) 10 | rescue StandardError 11 | nil 12 | end 13 | return nil unless doc && doc.errors.count.zero? 14 | 15 | process = begin 16 | doc.at_xpath("//bpmn:process") 17 | rescue StandardError 18 | nil 19 | end 20 | return nil unless process 21 | 22 | return if process.blank? 23 | 24 | process.elements.map { |el| factory(el) }.compact 25 | end 26 | 27 | private 28 | 29 | def factory(element) 30 | case element.name 31 | when "startEvent" 32 | Bpmn::Tokens::StartEvent.new(element) 33 | when "endEvent" 34 | Bpmn::Tokens::EndEvent.new(element) 35 | when "task" 36 | Bpmn::Tokens::Task.new(element) 37 | when "userTask" 38 | Bpmn::Tokens::UserTask.new(element) 39 | when "scriptTask" 40 | Bpmn::Tokens::ScriptTask.new(element) 41 | when "parallelGateway" 42 | Bpmn::Tokens::ParallelGateway.new(element) 43 | when "exclusiveGateway" 44 | Bpmn::Tokens::ExclusiveGateway.new(element) 45 | when "inclusiveGateway" 46 | Bpmn::Tokens::InclusiveGateway.new(element) 47 | when "sequenceFlow" 48 | Bpmn::Tokens::SequenceFlow.new(element) 49 | else 50 | raise NotImplementedError, "#{element.name} is unsupported yet." 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/common_token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::CommonToken < Bpmn::Tokens::Token 5 | attr_reader :incoming_ids, :outgoing_ids 6 | 7 | def initialize(element) 8 | super 9 | 10 | @incoming_ids = element.xpath("bpmn:incoming").map(&:content) 11 | @outgoing_ids = element.xpath("bpmn:outgoing").map(&:content) 12 | end 13 | 14 | def to_hash 15 | super.merge( 16 | incoming_ids: incoming_ids, 17 | outgoing_ids: outgoing_ids 18 | ) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/end_event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::EndEvent < Bpmn::Tokens::CommonToken 5 | def event? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/exclusive_gateway.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::ExclusiveGateway < Bpmn::Tokens::CommonToken 5 | attr_reader :default_flow_id 6 | 7 | def initialize(element) 8 | super 9 | 10 | @default_flow_id = element["default"] 11 | end 12 | 13 | def gateway? 14 | true 15 | end 16 | 17 | def to_hash 18 | super.merge( 19 | default_flow_id: default_flow_id 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/extensions/condition_expression.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::Extensions::ConditionExpression 5 | attr_reader :language, :condition, :type 6 | 7 | def initialize(element) 8 | @language = element["language"] 9 | @condition = element.content 10 | @type = element["xsi:type"] 11 | end 12 | 13 | def self.factory(xelement) 14 | ce = xelement.at_xpath("bpmn:conditionExpression") 15 | return nil unless ce 16 | 17 | new(ce) 18 | end 19 | 20 | def to_hash 21 | { 22 | language: language, 23 | condition: condition, 24 | type: type 25 | } 26 | end 27 | alias to_h to_hash 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/inclusive_gateway.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::InclusiveGateway < Bpmn::Tokens::CommonToken 5 | attr_reader :default_flow_id 6 | 7 | def initialize(element) 8 | super 9 | 10 | @default_flow_id = element["default"] 11 | end 12 | 13 | def gateway? 14 | true 15 | end 16 | 17 | def to_hash 18 | super.merge( 19 | default_flow_id: default_flow_id 20 | ) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/parallel_gateway.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::ParallelGateway < Bpmn::Tokens::CommonToken 5 | def gateway? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/script_task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::ScriptTask < Bpmn::Tokens::CommonToken 5 | attr_reader :script_format, :script 6 | 7 | def initialize(element) 8 | super 9 | 10 | @script_format = element["scriptFormat"] 11 | @script = element.at_xpath("bpmn:script").try(:content) 12 | end 13 | 14 | def task? 15 | true 16 | end 17 | 18 | def to_hash 19 | super.merge( 20 | extensions: { 21 | script_format: script_format, 22 | script: script 23 | } 24 | ) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/sequence_flow.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::SequenceFlow < Bpmn::Tokens::Token 5 | attr_reader :source_id, :target_id 6 | attr_reader :condition_expression 7 | 8 | def initialize(element) 9 | super 10 | 11 | @source_id = element["sourceRef"] 12 | @target_id = element["targetRef"] 13 | 14 | @condition_expression = Bpmn::Tokens::Extensions::ConditionExpression.factory(element) 15 | end 16 | 17 | def flow? 18 | true 19 | end 20 | 21 | def to_hash 22 | super.merge( 23 | source_id: source_id, 24 | target_id: target_id, 25 | extensions: { 26 | condition_expression: condition_expression.to_hash 27 | } 28 | ) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/start_event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::StartEvent < Bpmn::Tokens::CommonToken 5 | def event? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::Task < Bpmn::Tokens::CommonToken 5 | def task? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::Token 5 | attr_reader :id, :name, :node_type 6 | attr_reader :documentation 7 | 8 | def initialize(element) 9 | # @element = element 10 | @id = element["id"] 11 | @name = element["name"] || "" 12 | @node_type = element.name.underscore.to_sym 13 | 14 | @documentation = element.at_xpath("bpmn:documentation")&.content || "" 15 | end 16 | 17 | def gateway? 18 | false 19 | end 20 | 21 | def event? 22 | false 23 | end 24 | 25 | def task? 26 | false 27 | end 28 | 29 | def flow? 30 | false 31 | end 32 | 33 | def to_hash 34 | { 35 | node_id: id, 36 | node_type: node_type, 37 | name: name 38 | } 39 | end 40 | alias to_h to_hash 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/dummy/app/lib/bpmn/tokens/user_task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bpmn 4 | class Tokens::UserTask < Bpmn::Tokens::CommonToken 5 | def task? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/lib/script_engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ScriptEngine 4 | class << self 5 | def engine 6 | @engine ||= ScriptCore::Engine.new Rails.root.join("mruby", "bin") 7 | end 8 | 9 | def run(string, payload: nil, instruction_quota_start: nil) 10 | sources = [ 11 | %w[preparing prepare], 12 | ["user", string], 13 | %w[packing_output pack_output] 14 | ] 15 | 16 | engine.eval sources, 17 | input: { 18 | configuration: { 19 | time_zone_offset: Time.zone.formatted_offset(false) 20 | }, 21 | payload: payload 22 | }, 23 | instruction_quota_start: instruction_quota_start, 24 | environment_variables: { "TZ" => Time.zone.name } 25 | end 26 | 27 | def run_inline(string, payload: nil, instruction_quota_start: nil) 28 | run "Output.value = module Main\n#{string}\nend", 29 | payload: payload, 30 | instruction_quota_start: instruction_quota_start 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationMailer < ActionMailer::Base 4 | default from: "from@example.com" 5 | layout "mailer" 6 | end 7 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationRecord < ActiveRecord::Base 4 | self.abstract_class = true 5 | 6 | include ActsAsDefaultValue 7 | include EnumAttributeLocalizable 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/enum_attribute_localizable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EnumAttributeLocalizable 4 | extend ActiveSupport::Concern 5 | 6 | module ClassMethods 7 | def human_enum_value(attribute, value, options = {}) 8 | parts = attribute.to_s.split(".") 9 | attribute = parts.pop.pluralize 10 | attributes_scope = "#{i18n_scope}.attributes" 11 | 12 | if parts.any? 13 | namespace = parts.join("/") 14 | defaults = lookup_ancestors.map do |klass| 15 | :"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}.#{value}" 16 | end 17 | defaults << :"#{attributes_scope}.#{namespace}.#{attribute}.#{value}" 18 | else 19 | defaults = lookup_ancestors.map do |klass| 20 | :"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}.#{value}" 21 | end 22 | end 23 | 24 | defaults << :"attributes.#{attribute}.#{value}" 25 | defaults << options.delete(:default) if options[:default] 26 | defaults << value.to_s.humanize 27 | 28 | options[:default] = defaults 29 | I18n.translate(defaults.shift, **options) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Acceptance 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attribute :acceptance, :boolean, default: false 9 | end 10 | 11 | def interpret_to(model, field_name, _accessibility, _options = {}) 12 | super 13 | return unless acceptance 14 | 15 | model.validates field_name, acceptance: true 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/confirmation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Confirmation 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attribute :confirmation, :boolean, default: false 9 | end 10 | 11 | def interpret_to(model, field_name, _accessibility, _options = {}) 12 | super 13 | return unless confirmation 14 | 15 | model.validates field_name, confirmation: true 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/exclusion.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Exclusion 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_one :exclusion, class_name: "Fields::Validations::Exclusion::ExclusionOptions" 9 | accepts_nested_attributes_for :exclusion 10 | 11 | after_initialize do 12 | build_exclusion unless exclusion 13 | end 14 | end 15 | 16 | def interpret_to(model, field_name, accessibility, options = {}) 17 | super 18 | exclusion&.interpret_to model, field_name, accessibility, options 19 | end 20 | 21 | class ExclusionOptions < FieldOptions 22 | attribute :message, :string, default: "" 23 | attribute :in, :string, default: [], array: true 24 | 25 | def interpret_to(model, field_name, _accessibility, _options = {}) 26 | return if self.in.empty? 27 | 28 | options = { in: self.in } 29 | options[:message] = message if message.present? 30 | 31 | model.validates field_name, exclusion: options, allow_blank: true 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/format.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Format 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_one :format, class_name: "Fields::Validations::Format::FormatOptions" 9 | accepts_nested_attributes_for :format 10 | 11 | after_initialize do 12 | build_format unless format 13 | end 14 | end 15 | 16 | def interpret_to(model, field_name, accessibility, options = {}) 17 | super 18 | format&.interpret_to model, field_name, accessibility, options 19 | end 20 | 21 | class FormatOptions < FieldOptions 22 | attribute :with, :string, default: "" 23 | attribute :message, :string, default: "" 24 | 25 | validate do 26 | Regexp.new(with) if with.present? 27 | rescue RegexpError 28 | errors.add :with, :invalid 29 | end 30 | 31 | def interpret_to(model, field_name, _accessibility, _options = {}) 32 | return if with.blank? 33 | 34 | with = Regexp.new(self.with) 35 | 36 | options = { with: with } 37 | options[:message] = message if message.present? 38 | 39 | model.validates field_name, format: options, allow_blank: true 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/inclusion.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Inclusion 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_one :inclusion, class_name: "Fields::Validations::Inclusion::InclusionOptions" 9 | accepts_nested_attributes_for :inclusion 10 | 11 | after_initialize do 12 | build_inclusion unless inclusion 13 | end 14 | end 15 | 16 | def interpret_to(model, field_name, accessibility, options = {}) 17 | super 18 | inclusion&.interpret_to model, field_name, accessibility, options 19 | end 20 | 21 | class InclusionOptions < FieldOptions 22 | attribute :message, :string, default: "" 23 | attribute :in, :string, default: [], array: true 24 | 25 | def interpret_to(model, field_name, _accessibility, _options = {}) 26 | return if self.in.empty? 27 | 28 | options = { in: self.in } 29 | options[:message] = message if message.present? 30 | 31 | model.validates field_name, inclusion: options, allow_blank: true 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/length.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Length 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_one :length, class_name: "Fields::Validations::Length::LengthOptions" 9 | accepts_nested_attributes_for :length 10 | 11 | after_initialize do 12 | build_length unless length 13 | end 14 | end 15 | 16 | def interpret_to(model, field_name, accessibility, options = {}) 17 | super 18 | length&.interpret_to model, field_name, accessibility, options 19 | end 20 | 21 | class LengthOptions < FieldOptions 22 | attribute :minimum, :integer, default: 0 23 | attribute :maximum, :integer, default: 0 24 | attribute :is, :integer, default: 0 25 | 26 | validates :minimum, :maximum, :is, 27 | numericality: { 28 | greater_than_or_equal_to: 0 29 | } 30 | validates :maximum, 31 | numericality: { 32 | greater_than: :minimum 33 | }, 34 | if: proc { |record| record.maximum <= record.minimum && record.maximum.positive? } 35 | 36 | validates :is, 37 | numericality: { 38 | equal_to: 0 39 | }, 40 | if: proc { |record| !record.maximum.zero? || !record.minimum.zero? } 41 | 42 | def interpret_to(model, field_name, _accessibility, _options = {}) 43 | return if minimum.zero? && maximum.zero? && is.zero? 44 | 45 | if is.positive? 46 | model.validates field_name, length: { is: is }, allow_blank: true 47 | return 48 | end 49 | 50 | options = {} 51 | options[:minimum] = minimum if minimum.positive? 52 | options[:maximum] = maximum if maximum.positive? 53 | return if options.empty? 54 | 55 | model.validates field_name, length: options, allow_blank: true 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/numericality.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Numericality 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_one :numericality, class_name: "Fields::Validations::Numericality::NumericalityOptions" 9 | accepts_nested_attributes_for :numericality 10 | 11 | after_initialize do 12 | build_numericality unless numericality 13 | end 14 | end 15 | 16 | def interpret_to(model, field_name, accessibility, options = {}) 17 | super 18 | numericality&.interpret_to model, field_name, accessibility, options 19 | end 20 | 21 | class NumericalityOptions < FieldOptions 22 | attribute :lower_bound_check, :string, default: "disabled" 23 | attribute :upper_bound_check, :string, default: "disabled" 24 | 25 | attribute :lower_bound_value, :float, default: 0.0 26 | attribute :upper_bound_value, :float, default: 0.0 27 | 28 | enum lower_bound_check: { 29 | disabled: "disabled", 30 | greater_than: "greater_than", 31 | greater_than_or_equal_to: "greater_than_or_equal_to" 32 | }, _prefix: :lower_bound_check 33 | enum upper_bound_check: { 34 | disabled: "disabled", 35 | less_than: "less_than", 36 | less_than_or_equal_to: "less_than_or_equal_to" 37 | }, _prefix: :upper_bound_check 38 | 39 | validates :upper_bound_value, 40 | numericality: { 41 | greater_than: :lower_bound_value 42 | }, 43 | if: proc { upper_bound_check != "disabled" && lower_bound_check != "disabled" } 44 | 45 | def interpret_to(model, field_name, _accessibility, _options = {}) 46 | options = {} 47 | options[lower_bound_check] = lower_bound_value unless lower_bound_check_disabled? 48 | options[upper_bound_check] = upper_bound_value unless upper_bound_check_disabled? 49 | return if options.empty? 50 | 51 | options.symbolize_keys! 52 | model.validates field_name, numericality: options, allow_blank: true 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/fields/validations/presence.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | module Validations::Presence 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attribute :presence, :boolean, default: false 9 | end 10 | 11 | def interpret_to(model, field_name, _accessibility, _options = {}) 12 | super 13 | return unless presence 14 | 15 | model.validates field_name, presence: true 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/transitions/options/assignable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | module Assignable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attribute :assignee_user_ids, type: :string, array: true 9 | attribute :assign_to, type: :string, default: "creator" 10 | 11 | enum assign_to: { 12 | creator: "creator", 13 | specific: "specific", 14 | inherited: "inherited" 15 | }, _prefix: :assign_to 16 | 17 | validates :assign_to, 18 | presence: true 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/transitions/options/field_overridable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | module FieldOverridable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | embeds_many :field_overrides, class_name: "Transitions::Options::FieldOverridable::FieldOverride" 9 | accepts_nested_attributes_for :field_overrides 10 | end 11 | 12 | class FieldOverride < FieldOptions 13 | attribute :name, :string 14 | attribute :accessibility, :integer, default: 0 15 | 16 | attr_readonly :name 17 | enum accessibility: { read_and_write: 0, readonly: 1, hidden: 2 }, 18 | _prefix: :access 19 | 20 | validates :name, 21 | presence: true 22 | 23 | validates :accessibility, 24 | inclusion: { in: accessibilities.keys.map(&:to_sym) } 25 | 26 | def name 27 | self[:name]&.to_sym 28 | end 29 | 30 | def accessibility 31 | self[:accessibility]&.to_sym 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/transitions/options/votable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | module Votable 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | attribute :vote_rule, type: :string, default: "one_vote_pass" 9 | 10 | enum vote_rule: { 11 | one_vote_pass: "one_vote_pass" 12 | } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/dummy/app/models/field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Field < ApplicationRecord 4 | include FormCore::Concerns::Models::Field 5 | 6 | self.table_name = "fields" 7 | 8 | belongs_to :form, class_name: "MetalForm", foreign_key: "form_id", touch: true 9 | 10 | has_one :nested_form, as: :attachable 11 | 12 | validates :label, 13 | presence: true 14 | validates :type, 15 | inclusion: { 16 | in: ->(_) { Field.descendants.map(&:to_s) } 17 | }, 18 | allow_blank: false 19 | 20 | default_value_for :name, 21 | ->(_) { "field_#{SecureRandom.hex(3)}" }, 22 | allow_nil: false 23 | 24 | def self.type_key 25 | model_name.name.split("::").last.underscore 26 | end 27 | 28 | def type_key 29 | self.class.type_key 30 | end 31 | 32 | def options_configurable? 33 | options.is_a?(FieldOptions) && options.attributes.any? 34 | end 35 | 36 | def validations_configurable? 37 | validations.is_a?(FieldOptions) && validations.attributes.any? 38 | end 39 | 40 | def attached_nested_form? 41 | false 42 | end 43 | 44 | protected 45 | 46 | def interpret_validations_to(model, accessibility, overrides = {}) 47 | return unless accessibility == :read_and_write 48 | 49 | validations_overrides = overrides.fetch(:validations) { {} } 50 | validations = 51 | if validations_overrides.any? 52 | self.validations.dup.update(validations_overrides) 53 | else 54 | self.validations 55 | end 56 | 57 | validations.interpret_to(model, name, accessibility) 58 | end 59 | 60 | def interpret_extra_to(model, accessibility, overrides = {}) 61 | options_overrides = overrides.fetch(:options) { {} } 62 | options = 63 | if options_overrides.any? 64 | self.options.dup.update(options_overrides) 65 | else 66 | self.options 67 | end 68 | 69 | options.interpret_to(model, name, accessibility) 70 | end 71 | end 72 | 73 | require_dependency "fields" 74 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | %w[ 5 | text boolean decimal integer 6 | nested_form multiple_nested_form 7 | ].each do |type| 8 | require_dependency "fields/#{type}_field" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/boolean_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class BooleanField < Field 5 | serialize :validations, Validations::BooleanField 6 | serialize :options, NonConfigurable 7 | 8 | def stored_type 9 | :boolean 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/decimal_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class DecimalField < Field 5 | serialize :validations, Validations::DecimalField 6 | serialize :options, Options::DecimalField 7 | 8 | def stored_type 9 | :decimal 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/integer_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class IntegerField < Field 5 | serialize :validations, Validations::IntegerField 6 | serialize :options, Options::IntegerField 7 | 8 | def stored_type 9 | :integer 10 | end 11 | 12 | protected 13 | 14 | def interpret_extra_to(model, accessibility, _overrides = {}) 15 | return if accessibility != :read_and_write 16 | 17 | model.validates name, numericality: { only_integer: true }, allow_blank: true 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/multiple_nested_form_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class MultipleNestedFormField < Field 5 | after_create do 6 | build_nested_form.save! 7 | end 8 | 9 | serialize :validations, Validations::MultipleNestedFormField 10 | serialize :options, NonConfigurable 11 | 12 | def attached_nested_form? 13 | true 14 | end 15 | 16 | def interpret_to(model, overrides: {}) 17 | check_model_validity!(model) 18 | 19 | accessibility = overrides.fetch(:accessibility, self.accessibility) 20 | return model if accessibility == :hidden 21 | 22 | overrides[:name] = name 23 | 24 | nested_model = nested_form.to_virtual_model(overrides: { _global: { accessibility: accessibility } }) 25 | 26 | model.nested_models[name] = nested_model 27 | 28 | model.embeds_many name, anonymous_class: nested_model, validate: true 29 | model.accepts_nested_attributes_for name, reject_if: :all_blank 30 | model.attr_readonly name if accessibility == :readonly 31 | 32 | interpret_validations_to model, accessibility, overrides 33 | interpret_extra_to model, accessibility, overrides 34 | 35 | model 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/nested_form_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class NestedFormField < Field 5 | after_create do 6 | build_nested_form.save! 7 | end 8 | 9 | serialize :validations, Validations::NestedFormField 10 | serialize :options, NonConfigurable 11 | 12 | def attached_nested_form? 13 | true 14 | end 15 | 16 | def interpret_to(model, overrides: {}) 17 | check_model_validity!(model) 18 | 19 | accessibility = overrides.fetch(:accessibility, self.accessibility) 20 | return model if accessibility == :hidden 21 | 22 | nested_model = nested_form.to_virtual_model(overrides: { _global: { accessibility: accessibility } }) 23 | 24 | model.nested_models[name] = nested_model 25 | 26 | model.embeds_one name, anonymous_class: nested_model, validate: true 27 | model.accepts_nested_attributes_for name, reject_if: :all_blank 28 | model.attr_readonly name if accessibility == :readonly 29 | 30 | interpret_validations_to model, accessibility, overrides 31 | interpret_extra_to model, accessibility, overrides 32 | 33 | model 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/options/decimal_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Options 4 | class DecimalField < FieldOptions 5 | attribute :step, :decimal, default: 0.01 6 | 7 | validates :step, 8 | numericality: { 9 | greater_than_or_equal_to: 0.0 10 | } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/options/integer_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Options 4 | class IntegerField < FieldOptions 5 | attribute :step, :integer, default: 0 6 | 7 | validates :step, 8 | numericality: { 9 | only_integer: true, 10 | greater_than_or_equal_to: 0 11 | } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/options/text_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Options 4 | class TextField < FieldOptions 5 | attribute :multiline, :boolean, default: false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/text_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class TextField < Field 5 | serialize :validations, Validations::TextField 6 | serialize :options, Options::TextField 7 | 8 | def stored_type 9 | :string 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/boolean_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class BooleanField < FieldOptions 5 | prepend Fields::Validations::Acceptance 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/decimal_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class DecimalField < FieldOptions 5 | prepend Fields::Validations::Presence 6 | prepend Fields::Validations::Numericality 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/integer_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class IntegerField < FieldOptions 5 | prepend Fields::Validations::Presence 6 | prepend Fields::Validations::Numericality 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/multiple_nested_form_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class MultipleNestedFormField < FieldOptions 5 | prepend Fields::Validations::Presence 6 | prepend Fields::Validations::Length 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/nested_form_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class NestedFormField < FieldOptions 5 | prepend Fields::Validations::Presence 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/fields/validations/text_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields::Validations 4 | class TextField < FieldOptions 5 | prepend Fields::Validations::Presence 6 | prepend Fields::Validations::Length 7 | prepend Fields::Validations::Format 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/models/form.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Form < MetalForm 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/group.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Group < ApplicationRecord 4 | has_many :users, dependent: :nullify 5 | 6 | validates :name, 7 | presence: true 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/metal_form.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class MetalForm < ApplicationRecord 4 | include FormCore::Concerns::Models::Form 5 | 6 | self.table_name = "forms" 7 | 8 | has_many :fields, foreign_key: "form_id", dependent: :destroy 9 | 10 | default_value_for :name, 11 | ->(_) { "field_#{SecureRandom.hex(3)}" }, 12 | allow_nil: false 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/models/nested_form.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NestedForm < MetalForm 4 | belongs_to :attachable, polymorphic: true, touch: true 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/app/models/non_configurable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NonConfigurable < FieldOptions 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/place.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Place < WorkflowCore::Place 4 | def builtin? 5 | false 6 | end 7 | 8 | def append_to_graph(g, prev: nil, arc_label: "", nodes: {}) 9 | key = "p_#{id}" 10 | unless nodes[key] 11 | nodes[key] = Graphviz::Node.new("p.#{id}#{": #{name}" if name.present?}", shape: "box", style: "rounded") 12 | g << nodes[key] 13 | end 14 | current = nodes[key] 15 | 16 | prev.connect(current, label: arc_label) if prev && !prev.connected?(current) 17 | 18 | if output_transition 19 | next_node = nodes["t_#{output_transition.id}"] 20 | if !next_node 21 | output_transition.append_to_graph(g, prev: current, nodes: nodes) 22 | else 23 | current.connect(next_node, label: arc_label) 24 | end 25 | end 26 | 27 | g 28 | end 29 | end 30 | 31 | require_dependency "places" 32 | -------------------------------------------------------------------------------- /test/dummy/app/models/places.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Places 4 | %w[ 5 | start end 6 | ].each do |type| 7 | require_dependency "places/#{type}_place" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/models/places/end_place.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Places::EndPlace < Place 4 | def builtin? 5 | true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/places/start_place.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Places::StartPlace < Place 4 | def builtin? 5 | true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Token < WorkflowCore::Token 4 | belongs_to :assignable, polymorphic: true, optional: true 5 | belongs_to :forwardable, polymorphic: true, optional: true 6 | 7 | serialize :payload, Payload 8 | 9 | class Payload < FieldOptions 10 | attribute :note, type: :string 11 | attribute :action, type: :string 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/models/token/payload.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Token 4 | class Payload < FieldOptions 5 | attribute :note, type: :string 6 | attribute :action, type: :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/transition.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transition < WorkflowCore::Transition 4 | serialize :options, NonConfigurable 5 | 6 | def auto_forwardable? 7 | false 8 | end 9 | 10 | def options_configurable? 11 | options.attributes.any? || options.class.reflections.any? 12 | end 13 | 14 | def self.type_key 15 | model_name.name.split("::").last.underscore 16 | end 17 | 18 | def type_key 19 | self.class.type_key 20 | end 21 | 22 | def append_to_graph(g, prev: nil, arc_label: "", nodes: {}) 23 | key = "t_#{id}" 24 | unless nodes[key] 25 | nodes[key] = Graphviz::Node.new("t.#{id}#{": #{name}" if name.present?}", shape: "box") 26 | g << nodes[key] 27 | end 28 | current = nodes[key] 29 | 30 | prev.connect(current, label: arc_label) if prev && !prev.connected?(current) 31 | 32 | output_places.each do |succ| 33 | succ.append_to_graph(g, prev: current, nodes: nodes) 34 | end 35 | 36 | g 37 | end 38 | 39 | protected 40 | 41 | def auto_forward(next_token, transaction_options, **options) 42 | transition = next_token.place.output_transition 43 | return unless transition 44 | return unless transition.auto_forwardable? 45 | 46 | transition.on_fire(next_token, transaction_options, options) 47 | end 48 | end 49 | 50 | require_dependency "transitions" 51 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions 4 | %w[ 5 | sequence 6 | start end 7 | parallel_split synchronization 8 | exclusive_choice simple_merge 9 | ].each do |type| 10 | require_dependency "transitions/#{type}" 11 | end 12 | 13 | %w[ 14 | user_task assigning_assignees decision 15 | ].each do |type| 16 | require_dependency "transitions/variants/#{type}" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/end.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::End < Transition 4 | def on_fire(token, _transaction_options, **_options) 5 | token.completed! 6 | token.instance.completed! 7 | end 8 | 9 | def auto_forwardable? 10 | true 11 | end 12 | 13 | def options_configurable? 14 | false 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/exclusive_choice.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::ExclusiveChoice < Transition 4 | serialize :options, Transitions::Options::ExclusiveChoice 5 | 6 | def on_fire(token, transaction_options, **options) 7 | instance = token.instance 8 | 9 | next_place_id = self.options.conditions.select do |condition| 10 | r = ScriptEngine.run_inline condition.condition_expression, payload: instance.payload 11 | raise r.errors.map(&:message).join("; ") if r.errors.any? 12 | 13 | r.output 14 | end.first&.place_id || self.options.default_next_place_id 15 | next_place = workflow.places.find(next_place_id) 16 | 17 | token.completed! 18 | 19 | next_token = next_place.tokens.create! previous: token, type: "Token", 20 | instance: token.instance, workflow: workflow 21 | auto_forward(next_token, transaction_options, options) 22 | end 23 | 24 | def auto_forwardable? 25 | true 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/options/assigning_assignees.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | class AssigningAssignees < FieldOptions 5 | include Assignable 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/options/decision.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | class Decision < FieldOptions 5 | include FieldOverridable 6 | include Votable 7 | 8 | embeds_many :actions, class_name: "Transitions::Options::Decision::Action" 9 | accepts_nested_attributes_for :actions, allow_destroy: true 10 | 11 | class Action < FieldOptions 12 | attribute :text, :string 13 | attribute :value, :string 14 | attribute :place_id, :integer 15 | 16 | validates :text, :value, :place_id, 17 | presence: true 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/options/exclusive_choice.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | class ExclusiveChoice < FieldOptions 5 | attribute :default_next_place_id, :integer 6 | 7 | embeds_many :conditions, class_name: "Transitions::Options::ExclusiveChoice::Condition" 8 | accepts_nested_attributes_for :conditions 9 | 10 | validates :default_next_place_id, 11 | presence: true 12 | 13 | class Condition < FieldOptions 14 | attribute :condition_expression, :string 15 | attribute :place_id, :integer 16 | 17 | validates :condition_expression, :place_id, 18 | presence: true 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/options/user_task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Options 4 | class UserTask < FieldOptions 5 | include FieldOverridable 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/parallel_split.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::ParallelSplit < Transition 4 | def on_fire(token, transaction_options, **options) 5 | token.completed! 6 | output_places.each do |p| 7 | next_token = p.tokens.create! previous: token, type: "Token", 8 | instance: token.instance, workflow: workflow 9 | auto_forward(next_token, transaction_options, options) 10 | end 11 | end 12 | 13 | def auto_forwardable? 14 | true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/sequence.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::Sequence < Transition 4 | def on_fire(token, transaction_options, **options) 5 | p = output_places.first # assume only one output place 6 | 7 | token.completed! 8 | next_token = p.tokens.create! previous: token, type: "Token", 9 | instance: token.instance, workflow: workflow 10 | auto_forward(next_token, transaction_options, options) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/simple_merge.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::SimpleMerge < Transition 4 | def on_fire(token, transaction_options, **options) 5 | p = output_places.first # assume only one output place 6 | # return unless p.tokens.size.zero? 7 | 8 | token.completed! 9 | # TODO: consider if status are not completed 10 | completed_tokens = 11 | input_places 12 | .includes(:tokens).where(workflow_tokens: { instance_id: token.instance_id }) 13 | .map(&:tokens).flatten.select(&:completed?) 14 | if completed_tokens.size == 1 15 | next_token = p.tokens.create! previous: token, type: "Token", 16 | instance: token.instance, workflow: workflow 17 | auto_forward(next_token, transaction_options, options) 18 | end 19 | end 20 | 21 | def auto_forwardable? 22 | true 23 | end 24 | 25 | def options_configurable? 26 | false 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/start.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::Start < Transition 4 | # http://workflowpatterns.com/patterns/control/basic/wcp1.php 5 | 6 | def on_fire(token, transaction_options, **options) 7 | p = output_places.first # assume only one output place 8 | 9 | token.completed! 10 | next_token = p.tokens.create! previous: token, type: "Token", 11 | instance: token.instance, workflow: workflow 12 | auto_forward(next_token, transaction_options, options) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/synchronization.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Transitions::Synchronization < Transition 4 | def on_fire(token, transaction_options, **options) 5 | p = output_places.first # assume only one output place 6 | # return unless p.tokens.size.zero? 7 | 8 | token.completed! 9 | # TODO: consider if status are not completed 10 | completed_tokens = 11 | input_places 12 | .includes(:tokens).where(workflow_tokens: { instance_id: token.instance_id }) 13 | .map(&:tokens).flatten.select(&:completed?) 14 | if completed_tokens.size == input_places.size 15 | next_token = p.tokens.create! previous: token, type: "Token", 16 | instance: token.instance, workflow: workflow 17 | auto_forward(next_token, transaction_options, options) 18 | end 19 | end 20 | 21 | def auto_forwardable? 22 | true 23 | end 24 | 25 | def options_configurable? 26 | false 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/variants/assigning_assignees.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Variants 4 | class AssigningAssignees < Transitions::Sequence 5 | serialize :options, Transitions::Options::AssigningAssignees 6 | 7 | def on_fire(token, transaction_options, **options) 8 | token.completed! 9 | 10 | place = output_places.first # assume only one output place 11 | if self.options.assign_to_creator? 12 | next_token = place.tokens.create! assignable: token.instance.creator, 13 | previous: token, type: "Token", 14 | instance: token.instance, workflow: workflow 15 | auto_forward(next_token, transaction_options, options) 16 | elsif self.options.assign_to_specific? 17 | assignees = User.where(id: self.options.assignee_user_ids).to_a 18 | assignees.each do |assignee| 19 | next_token = place.tokens.create! assignable: assignee, 20 | previous: token, type: "Token", 21 | instance: token.instance, workflow: workflow 22 | auto_forward(next_token, transaction_options, options) 23 | end 24 | elsif self.options.assign_to_inherited? 25 | next_token = place.tokens.create! assignable: token.assignable, 26 | previous: token, type: "Token", 27 | instance: token.instance, workflow: workflow 28 | auto_forward(next_token, transaction_options, options) 29 | else 30 | raise "Unsupported assign to #{options.assign_to}" 31 | end 32 | end 33 | 34 | def auto_forwardable? 35 | true 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/variants/decision.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Variants 4 | class Decision < Transitions::ExclusiveChoice 5 | serialize :options, Transitions::Options::Decision 6 | 7 | def on_fire(token, transaction_options, **options) 8 | token.completed! 9 | 10 | if self.options.one_vote_pass? 11 | token.instance.tokens.where(workflow: workflow, place_id: token.place, status: "processing").update_all status: :terminated 12 | end 13 | 14 | next_place_id = self.options.actions.select do |action| 15 | token.payload["action"] == action.value 16 | end.first&.place_id 17 | raise "No suitable place id for action #{token.payload['action']}" unless next_place_id 18 | 19 | next_place = workflow.places.find(next_place_id) 20 | next_token = next_place.tokens.create! previous: token, type: "Token", 21 | instance: token.instance, workflow: workflow, 22 | assignable: token.assignable 23 | auto_forward(next_token, transaction_options, options) 24 | end 25 | 26 | def auto_forwardable? 27 | false 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/dummy/app/models/transitions/variants/user_task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Transitions::Variants 4 | class UserTask < Transitions::Sequence 5 | serialize :options, Transitions::Options::UserTask 6 | 7 | def on_fire(token, transaction_options, **options) 8 | token.completed! 9 | token.instance.tokens.where(workflow: workflow, place_id: token.place, status: "processing").update_all status: :terminated 10 | 11 | place = output_places.first # assume only one output place 12 | next_token = place.tokens.create! previous: token, type: "Token", 13 | instance: token.instance, workflow: workflow, 14 | assignable: token.assignable 15 | auto_forward(next_token, transaction_options, options) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class User < ApplicationRecord 4 | belongs_to :group, optional: true 5 | 6 | validates :name, 7 | presence: true 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/app/models/virtual_model.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_dependency "concerns/enum_attribute_localizable" 4 | 5 | class VirtualModel < FormCore::VirtualModel 6 | include FormCore::ActsAsDefaultValue 7 | 8 | include EnumAttributeLocalizable 9 | 10 | def persisted? 11 | false 12 | end 13 | 14 | class << self 15 | def nested_models 16 | @nested_models ||= {} 17 | end 18 | 19 | def attr_readonly?(attr_name) 20 | readonly_attributes.include? attr_name.to_s 21 | end 22 | 23 | def metadata 24 | @metadata ||= {} 25 | end 26 | 27 | def _embeds_reflections 28 | _reflections.select { |_, v| v.is_a? ActiveEntity::Reflection::EmbeddedAssociationReflection } 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/dummy/app/models/workflow_instance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class WorkflowInstance < WorkflowCore::WorkflowInstance 4 | belongs_to :creator, class_name: "User" 5 | 6 | after_create :auto_create_start_token! 7 | 8 | private 9 | 10 | def auto_create_start_token! 11 | tokens.create! place: workflow.start_place, 12 | workflow: workflow, 13 | type: "Token", 14 | assignable: creator 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/application_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationPresenter < SimpleDelegator 4 | def initialize(model, view, options = {}) 5 | super(model) 6 | 7 | @model = model 8 | @view = view 9 | @options = options 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/concerns/fields/presenter_for_number_field.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Concerns::Fields 4 | module PresenterForNumberField 5 | extend ActiveSupport::Concern 6 | 7 | def min 8 | return if @model.validations.numericality.lower_bound_check_disabled? 9 | 10 | min = @model.validations.numericality.lower_bound_value 11 | integer_only? ? min.to_i : min 12 | end 13 | 14 | def max 15 | return if @model.validations.numericality.upper_bound_check_disabled? 16 | 17 | max = @model.validations.numericality.upper_bound_value 18 | integer_only? ? max.to_i : max 19 | end 20 | 21 | def step 22 | step = @model.options.step 23 | return if step.zero? 24 | 25 | integer_only? ? step.to_i : step 26 | end 27 | 28 | def integer_only? 29 | false 30 | end 31 | 32 | def to_builder_options 33 | { min: min, max: max, step: step, required: required? }.reject { |_, v| v.blank? } 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/boolean_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class BooleanFieldPresenter < FieldPresenter 5 | def required 6 | @model.validations&.acceptance 7 | end 8 | 9 | def value_for_preview 10 | super ? I18n.t("values.true") : I18n.t("values.false") 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/composite_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class CompositeFieldPresenter < FieldPresenter 5 | def value 6 | target&.send(@model.name) 7 | end 8 | 9 | def value_for_preview 10 | target&.send(@model.name) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/decimal_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class DecimalFieldPresenter < FieldPresenter 5 | include Concerns::Fields::PresenterForNumberField 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Fields::FieldPresenter < ApplicationPresenter 4 | def required 5 | @model.validations&.presence 6 | end 7 | alias required? required 8 | 9 | def target 10 | @options[:target] 11 | end 12 | 13 | def value 14 | target&.read_attribute(@model.name) 15 | end 16 | 17 | def value_for_preview 18 | target&.read_attribute(@model.name) 19 | end 20 | 21 | def access_readonly? 22 | target.class.attr_readonly?(@model.name.to_s) 23 | end 24 | 25 | def access_hidden? 26 | target.class.attribute_names.exclude?(@model.name.to_s) && target.class._reflections.keys.exclude?(@model.name.to_s) 27 | end 28 | 29 | def access_read_and_write? 30 | !access_readonly? && 31 | (target.class.attribute_names.include?(@model.name.to_s) || target.class._reflections.key?(@model.name.to_s)) 32 | end 33 | 34 | def id 35 | "form_field_#{@model.id}" 36 | end 37 | 38 | def nested_form_field? 39 | false 40 | end 41 | 42 | def multiple_nested_form? 43 | false 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/integer_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class IntegerFieldPresenter < FieldPresenter 5 | include Concerns::Fields::PresenterForNumberField 6 | 7 | def integer_only? 8 | true 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/multiple_nested_form_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class MultipleNestedFormFieldPresenter < CompositeFieldPresenter 5 | def multiple_nested_form? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/nested_form_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class NestedFormFieldPresenter < CompositeFieldPresenter 5 | def nested_form_field? 6 | true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/app/presenters/fields/text_field_presenter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Fields 4 | class TextFieldPresenter < FieldPresenter 5 | def multiline 6 | @model.options.multiline 7 | end 8 | alias multiline? multiline 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/field_options/_decimal_field.html.erb: -------------------------------------------------------------------------------- 1 |

Decimal field

2 | 3 |
4 | <%= f.label :step, class: "label" %> 5 |
6 | <%= f.number_field :step, class: "input", min: 0, step: 0.01 %> 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/field_options/_integer_field.html.erb: -------------------------------------------------------------------------------- 1 |

Integer field

2 | 3 |
4 | <%= f.label :step, class: "label" %> 5 |
6 | <%= f.number_field :step, class: "input", min: 0, step: 1 %> 7 |
8 |
9 | 10 |
11 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/field_options/_text_field.html.erb: -------------------------------------------------------------------------------- 1 |

Text field

2 | 3 |
4 |
5 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_boolean_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 | <% if field.hint.present? %> 9 |

<%= field.hint %>

10 | <% end %> 11 |
12 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_decimal_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= f.label field.name, field.label, class: 'label' %> 3 |
4 | <%= f.number_field field.name, class: 'input', id: field.id, required: field.required, disabled: field.access_readonly?, **field.to_builder_options %> 5 |
6 | <% if field.hint.present? %> 7 |

<%= field.hint %>

8 | <% end %> 9 |
10 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_integer_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= f.label field.name, field.label, class: 'label' %> 3 |
4 | <%= f.number_field field.name, class: 'input', id: field.id, required: field.required, disabled: field.access_readonly?, **field.to_builder_options %> 5 |
6 | <% if field.hint.present? %> 7 |

<%= field.hint %>

8 | <% end %> 9 |
10 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_multiple_nested_form_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= field.label %>

3 | 4 | <%= tag.div id: field.name, class: "collection" do %> 5 | <%= f.fields_for field.name do |ff| %> 6 | <%= render "_form_core/fields/nested_form", f: ff, field: field, form: field.nested_form, nesting: false %> 7 | <% end %> 8 | 9 | 22 | <% end %> 23 |
24 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_nested_form.html.erb: -------------------------------------------------------------------------------- 1 | <% nesting ||= false %> 2 | 3 |
4 | <% if f.object.errors.any? %> 5 |
6 |
7 |

8 | <%= pluralize(f.object.errors.count, "error") %> prohibited this form from being submitted: 9 |

10 |
11 |
12 |
    13 | <% f.object.errors.messages.each do |name, messages| %> 14 | <% messages.each do |message| %> 15 |
  • 16 | <%= "#{form.fields.select { |field| field.name == name}.first&.label } #{message}" %> 17 |
  • 18 | <% end %> 19 | <% end %> 20 |
21 |
22 |
23 | <% end %> 24 | 25 | <% form.fields.each do |field| %> 26 | <% field = present(field, target: f.object) %> 27 | <% unless field.access_hidden? %> 28 | <%= render "_form_core/fields/#{field.type_key}", f: f, field: field, nesting: nesting %> 29 | <% end %> 30 | <% end %> 31 | 32 | <% if field.access_read_and_write? %> 33 |
34 |
35 | <%= link_to_remove_association "Remove", f, class: "button is-small is-danger", wrapper_class: "nested_form" %> 36 |
37 |
38 | <% end %> 39 |
40 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_nested_form_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= field.label %>

3 | 4 | <%= tag.div id: field.name, class: "collection" do %> 5 | <%= f.fields_for field.name do |ff| %> 6 | <%= render "_form_core/fields/nested_form", f: ff, field: field, form: field.nested_form, nesting: false %> 7 | <% end %> 8 | 9 | 23 | <% end %> 24 |
25 | 26 | <% if field.access_read_and_write? %> 27 | 42 | <% end %> 43 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/fields/_text_field.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= f.label field.name, field.label, class: 'label' %> 3 |
4 | <% if field.options.multiline %> 5 | <%= f.text_area field.name, class: 'textarea', id: field.id, required: field.required, disabled: field.access_readonly? %> 6 | <% else %> 7 | <%= f.text_field field.name, class: 'input', id: field.id, required: field.required, disabled: field.access_readonly? %> 8 | <% end %> 9 |
10 | <% if field.hint.present? %> 11 |

<%= field.hint %>

12 | <% end %> 13 |
14 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/preview/_form.html.erb: -------------------------------------------------------------------------------- 1 | <% form.fields.map { |field| present(field, target: instance) }.each do |field| %> 2 | <% if field.nested_form_field? %> 3 | <% next unless field.value_for_preview %> 4 | 5 |

<%= field.label %>:

6 | <%= render "_form_core/preview/nested_form", form: field.nested_form, instance: field.value_for_preview %> 7 | <% elsif field.multiple_nested_form? %> 8 | <% next if field.value_for_preview.empty? %> 9 | 10 |

<%= field.label %>:

11 | <%= field.value_for_preview.map do |nested_instance| %> 12 | <% render "_form_core/preview/nested_form", form: field.nested_form, instance: nested_instance %> 13 | <% end.join("
").html_safe %> 14 | <% else %> 15 |

<%= field.label %>: <%= field.value_for_preview %>

16 | <% end %> 17 | <% end %> 18 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/preview/_nested_form.html.erb: -------------------------------------------------------------------------------- 1 | <% fields = form.fields.map { |field| present(field, target: instance) }.reject(&:access_hidden?) %> 2 | <% return if fields.empty? %> 3 | 4 |
5 | <% fields.each do |field| %> 6 | <% if field.nested_form_field? %> 7 | <% next unless field.value_for_preview %> 8 | 9 |

<%= field.label %>:

10 | <%= render "_form_core/preview/nested_form", form: field.nested_form, instance: field.value_for_preview %> 11 | <% elsif field.multiple_nested_form? %> 12 | <% next if field.value_for_preview.empty? %> 13 | 14 |

<%= field.label %>:

15 | <%= field.value_for_preview.map do |nested_instance| %> 16 | <% render "_form_core/preview/nested_form", form: field.nested_form, instance: nested_instance %> 17 | <% end.join("
").html_safe %> 18 | <% else %> 19 |

<%= field.label %>: <%= field.value_for_preview %>

20 | <% end %> 21 | <% end %> 22 |
23 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/render/_form.html.erb: -------------------------------------------------------------------------------- 1 | <% options ||= {} %> 2 | <% back_url ||= url_for(:back) %> 3 | 4 | <%= form_with(model: instance, **options) do |f| %> 5 | <% if instance.errors.any? %> 6 |
7 |
8 |

9 | <%= pluralize(instance.errors.count, "error") %> prohibited this form from being submitted: 10 |

11 |
12 |
13 | 20 |
21 |
22 | <% end %> 23 | 24 | <% form.fields.each do |field| %> 25 | <% field = present(field, target: instance) %> 26 | <% next if field.access_hidden? %> 27 | 28 | <%= render "_form_core/fields/#{field.type_key}", f: f, field: field %> 29 | <% end %> 30 | 31 |
32 |
33 | <%= f.submit "Submit", class: "button is-primary" %> 34 |
35 |
36 | <%= link_to "Back", back_url, class: "button is-link" %> 37 |
38 |
39 | <% end %> 40 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_acceptance.html.erb: -------------------------------------------------------------------------------- 1 |

Acceptance

2 | 3 |
4 |
5 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_confirmation.html.erb: -------------------------------------------------------------------------------- 1 |

Confirmation

2 | 3 |
4 |
5 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_exclusion.html.erb: -------------------------------------------------------------------------------- 1 |

Exclusion

2 |
3 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_format.html.erb: -------------------------------------------------------------------------------- 1 | <% validations.build_format unless validations.format %> 2 | <% format = validations.format %> 3 | 4 |

Format

5 | 6 | <%= f.fields_for :format, format do |ff| %> 7 | <% if format.errors.any? %> 8 |
9 |
10 |

11 | <%= pluralize(format.errors.count, "error") %> prohibited this form from being saved: 12 |

13 |
14 |
15 | <% format.errors.full_messages.each do |message| %> 16 |
  • <%= message %>
  • 17 | <% end %> 18 |
    19 |
    20 | <% end %> 21 | 22 |
    23 | <%= ff.label :with %> 24 |
    25 | <%= ff.text_field :with, class: "input" %> 26 |
    27 |
    28 | 29 |
    30 | <%= ff.label :message %> 31 |
    32 | <%= ff.text_field :message, class: "input" %> 33 |
    34 |
    35 | <% end %> 36 | 37 |
    38 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_inclusion.html.erb: -------------------------------------------------------------------------------- 1 |

    Inclusion

    2 |
    3 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_length.html.erb: -------------------------------------------------------------------------------- 1 | <% validations.build_length unless validations.length %> 2 | <% length = validations.length %> 3 | 4 |

    Length

    5 | 6 | <%= f.fields_for :length, length do |ff| %> 7 | <% if length.errors.any? %> 8 |
    9 |
    10 |

    11 | <%= pluralize(length.errors.count, "error") %> prohibited this form from being saved: 12 |

    13 |
    14 |
    15 | <% length.errors.full_messages.each do |message| %> 16 |
  • <%= message %>
  • 17 | <% end %> 18 |
    19 |
    20 | <% end %> 21 | 22 |
    23 | <%= ff.label :minimum %> 24 |
    25 | <%= ff.number_field :minimum, min: 0, step: 1, class: 'input' %> 26 |
    27 |
    28 | 29 |
    30 | <%= ff.label :maximum %> 31 |
    32 | <%= ff.number_field :maximum, min: 0, step: 1, class: 'input' %> 33 |
    34 |
    35 | 36 |
    37 | <%= ff.label :is %> 38 |
    39 | <%= ff.number_field :is, min: 0, step: 1, class: 'input' %> 40 |
    41 |
    42 | <% end %> 43 | 44 |
    45 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_numericality.html.erb: -------------------------------------------------------------------------------- 1 | <% validations.build_numericality unless validations.numericality %> 2 | <% numericality = validations.numericality %> 3 | 4 |

    Numericality

    5 | 6 | <%= f.fields_for :numericality, numericality do |ff| %> 7 | <% if numericality.errors.any? %> 8 |
    9 |
    10 |

    11 | <%= pluralize(numericality.errors.count, "error") %> prohibited this form from being saved: 12 |

    13 |
    14 |
    15 | <% numericality.errors.full_messages.each do |message| %> 16 |
  • <%= message %>
  • 17 | <% end %> 18 |
    19 |
    20 | <% end %> 21 | 22 |
    23 |
    24 | 25 | <%= ff.select :lower_bound_check, options_for_enum_select(numericality.class, :lower_bound_check, numericality.lower_bound_check), {}, class: "select" %> 26 | 27 |
    28 |
    29 | <%= ff.number_field :lower_bound_value, step: 0.01, class: "input" %> 30 |
    31 |
    32 | 33 |
    34 |
    35 | 36 | <%= ff.select :upper_bound_check, options_for_enum_select(numericality.class, :upper_bound_check, numericality.upper_bound_check), {}, class: "select" %> 37 | 38 |
    39 |
    40 | <%= ff.number_field :upper_bound_value, step: 0.01, class: "input" %> 41 |
    42 |
    43 | <% end %> 44 | 45 |
    46 | -------------------------------------------------------------------------------- /test/dummy/app/views/_form_core/validations/_presence.html.erb: -------------------------------------------------------------------------------- 1 |

    Presence

    2 | 3 |
    4 |
    5 | 9 |
    10 |
    11 | 12 |
    13 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_action.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <% if f.object.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(f.object.errors.count, "error") %> prohibited this form from being submitted: 7 |

    8 |
    9 |
    10 |
      11 | <% f.object.errors.full_messages.each do |message| %> 12 |
    • 13 | <%= message %> 14 |
    • 15 | <% end %> 16 |
    17 |
    18 |
    19 | <% end %> 20 | 21 |
    22 | <%= f.label :text, class: "label" %> 23 |
    24 | <%= f.text_field :text, class: "input" %> 25 |
    26 |
    27 | 28 |
    29 | <%= f.label :value, class: "label" %> 30 |
    31 | <%= f.text_field :value, class: "input" %> 32 |
    33 |
    34 | 35 |
    36 | <%= f.label :place, class: "label" %> 37 |
    38 |
    39 | <%= f.collection_select :place_id, transition.output_places, :id, :id, include_blank: true %> 40 |
    41 |
    42 |
    43 | 44 |
    45 |
    46 | <%= link_to_remove_association "Remove", f, class: "button is-small is-danger", wrapper_class: "nested_form" %> 47 |
    48 |
    49 |
    50 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_assignable.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= f.label :assign_to %> 3 |
    4 | <% options.class.assign_tos.each do |k, _| %> 5 |
    6 | <%= f.radio_button :assign_to, k, class: "form-check-input", id: "assign_to_#{k}" %> 7 | <%= f.label :assign_to, options.class.human_enum_value(:assign_to, k), class: "form-check-label", for: "assign_to_#{k}" %> 8 |
    9 | <% end %> 10 |
    11 |
    12 | 13 |
    14 | <%= f.label :assignee_user_ids, class: "label" %> 15 |
    16 | 17 | <%= f.select :assignee_user_ids, options_from_collection_for_select(User.all, :id, :name, f.object.assignee_user_ids), {}, multiple: true %> 18 | 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_assigning_assignees.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "_workflow_core/transition_options/assignable", f: f, transition: transition, options: options %> 2 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_condition.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <% if f.object.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(f.object.errors.count, "error") %> prohibited this form from being submitted: 7 |

    8 |
    9 |
    10 |
      11 | <% f.object.errors.full_messages.each do |message| %> 12 |
    • 13 | <%= message %> 14 |
    • 15 | <% end %> 16 |
    17 |
    18 |
    19 | <% end %> 20 | 21 |
    22 | <%= f.label :condition_expression, class: "label" %> 23 |
    24 | <%= f.text_field :condition_expression, class: "input" %> 25 |
    26 |
    27 | 28 |
    29 | <%= f.label :place, class: "label" %> 30 |
    31 |
    32 | <%= f.collection_select :place_id, transition.output_places, :id, :id, include_blank: true %> 33 |
    34 |
    35 |
    36 | 37 |
    38 |
    39 | <%= link_to_remove_association "Remove", f, class: "button is-small is-danger", wrapper_class: "nested_form" %> 40 |
    41 |
    42 |
    43 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_decision.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "_workflow_core/transition_options/field_overridable", f: f, transition: transition, options: options %> 2 | 3 |
    4 |

    Actions

    5 | 6 |
    7 | <%= f.fields_for :actions do |ff| %> 8 | <%= render "_workflow_core/transition_options/action", f: ff, transition: transition %> 9 | <% end %> 10 | 11 | 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_exclusive_choice.html.erb: -------------------------------------------------------------------------------- 1 |

    Exclusive choice options

    2 | 3 |
    4 | <%= f.label :default_next_place, class: "label" %> 5 |
    6 |
    7 | <%= f.collection_select :default_next_place_id, transition.output_places, :id, :id, include_blank: true %> 8 |
    9 |
    10 |
    11 | 12 |
    13 |

    Conditions

    14 | 15 |
    16 | <%= f.fields_for :conditions do |ff| %> 17 | <%= render "_workflow_core/transition_options/condition", f: ff, transition: transition %> 18 | <% end %> 19 | 20 | 28 |
    29 |
    30 | 31 |
    32 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_field_overridable.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Field overrides

    3 | 4 | <% @workflow.form.fields.each do |field| %> 5 | <% field_override = options.field_overrides.find { |r| r.name == field.name } || options.field_overrides.build(name: field.name, accessibility: field.accessibility) %> 6 | <%= f.fields_for :field_overrides, field_override do |ff| %> 7 | <%= ff.hidden_field :name %> 8 |
    9 | <%= label_tag field.name, field.label, class: "label" %> 10 |
    11 | <% Field.accessibilities.each do |k, _| %> 12 | 16 | <% end %> 17 |
    18 |
    19 | <% end %> 20 | <% end %> 21 |
    22 | 23 |
    24 | -------------------------------------------------------------------------------- /test/dummy/app/views/_workflow_core/transition_options/_user_task.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "_workflow_core/transition_options/field_overridable", f: f, transition: transition, options: options %> 2 | -------------------------------------------------------------------------------- /test/dummy/app/views/groups/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: group, local: true) do |f| %> 2 | <% if group.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(group.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | <% group.errors.full_messages.each do |message| %> 11 |
  • <%= message %>
  • 12 | <% end %> 13 |
    14 |
    15 | <% end %> 16 | 17 |
    18 | <%= f.label :name, class: "label" %> 19 |
    20 | <%= f.text_field :name, id: :user_name, class: 'input' %> 21 |
    22 |
    23 | 24 |
    25 |
    26 | <%= f.submit class: "button is-primary" %> 27 |
    28 |
    29 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 30 |
    31 |
    32 | <% end %> 33 | -------------------------------------------------------------------------------- /test/dummy/app/views/groups/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing group

    4 | <%= render "form", group: @group %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/groups/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New group", new_group_path, class: "button is-primary" %> 5 |

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @groups.each do |group| %> 16 | 17 | 18 | 19 | 23 | 24 | <% end %> 25 | 26 |
    IdName
    <%= group.id %><%= group.name %> 20 | <%= link_to "Edit", edit_group_path(group) %> | 21 | <%= link_to "Destroy", group, method: :delete, data: {confirm: "Are you sure?"} %> 22 |
    27 |
    28 |
    29 | 30 | -------------------------------------------------------------------------------- /test/dummy/app/views/groups/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New group

    4 | <%= render "form", group: @group %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/_alert.html.erb: -------------------------------------------------------------------------------- 1 | <% return if alert.blank? %> 2 |
    3 |

    <%= alert %>

    4 |
    5 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/_nav.html.erb: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/_notice.html.erb: -------------------------------------------------------------------------------- 1 | <% return if notice.blank? %> 2 |
    3 |

    <%= notice %>

    4 |
    5 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Workflow Core 5 | 6 | <%= csrf_meta_tags %> 7 | 8 | 9 | 10 | 11 | <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %> 12 | <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %> 13 | 14 | 15 | <%= render "layouts/notice" %> 16 | <%= render "layouts/alert" %> 17 | <%= render "layouts/nav" %> 18 | 19 |
    20 | <%= content_for?(:content) ? yield(:content) : yield %> 21 |
    22 | 23 | <%= render "layouts/footer" %> 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/workflow_instances.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :sub_content do %> 2 |
    3 |
    4 | 12 |
    13 |
    14 | 15 | <%= yield %> 16 | <% end %> 17 | 18 | <%= render template: "layouts/workflows" %> 19 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/workflows.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :content do %> 2 |
    3 |
    4 |
    5 |

    6 | <%= @workflow.name %> 7 |

    8 |

    9 | <%= @workflow.description %> 10 |

    11 |
    12 |
    13 | 14 |
    15 |
    16 | 35 |
    36 |
    37 |
    38 | 39 | <%= content_for?(:sub_content) ? yield(:sub_content) : yield %> 40 | <% end %> 41 | 42 | <%= render template: "layouts/application" %> 43 | -------------------------------------------------------------------------------- /test/dummy/app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: user, local: true) do |f| %> 2 | <% if user.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(user.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | <% user.errors.full_messages.each do |message| %> 11 |
  • <%= message %>
  • 12 | <% end %> 13 |
    14 |
    15 | <% end %> 16 | 17 |
    18 | <%= f.label :name, class: "label" %> 19 |
    20 | <%= f.text_field :name, id: :user_name, class: 'input' %> 21 |
    22 |
    23 | 24 |
    25 | <%= f.label :group, class: "label" %> 26 |
    27 |
    28 | <%= f.collection_select :group_id, Group.all, :id, :name, include_blank: true %> 29 |
    30 |
    31 |
    32 | 33 |
    34 |
    35 | <%= f.submit class: "button is-primary" %> 36 |
    37 |
    38 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 39 |
    40 |
    41 | <% end %> 42 | -------------------------------------------------------------------------------- /test/dummy/app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing user

    4 | <%= render "form", user: @user %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New user", new_user_path, class: "button is-primary" %> 5 |

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @users.each do |user| %> 17 | 18 | 19 | 20 | 21 | 28 | 29 | <% end %> 30 | 31 |
    IdNameGroup
    <%= user.id %><%= user.name %><%= user.group&.name %> 22 | <% if current_user != user %> 23 | <%= link_to "Sign in", sessions_path(user_id: user.id), method: :post %> | 24 | <% end %> 25 | <%= link_to "Edit", edit_user_path(user) %> | 26 | <%= link_to "Destroy", user, method: :delete, data: {confirm: "Are you sure?"} %> 27 |
    32 |
    33 |
    34 | 35 | -------------------------------------------------------------------------------- /test/dummy/app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New user

    4 | <%= render "form", user: @user %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: workflow, local: true) do |f| %> 2 | <% if workflow.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(workflow.errors.count, "error") %> prohibited this workflow from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 |
    20 | <%= f.label :name, class: "label" %> 21 |
    22 | <%= f.text_field :name, class: "input", placeholder: "Name" %> 23 |
    24 |
    25 | 26 |
    27 | <%= f.label :description, class: "label" %> 28 |
    29 | <%= f.text_area :description, class: "textarea", placeholder: "Description" %> 30 |
    31 |
    32 | 33 |
    34 |
    35 | <%= f.submit class: "button is-primary" %> 36 |
    37 |
    38 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 39 |
    40 |
    41 | <% end %> 42 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing workflow

    4 | <%= render "form", workflow: @workflow %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: [@workflow, field.becomes(Field)], scope: :field, local: true) do |f| %> 2 | <% if field.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(field.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 |
    20 | <%= f.label :name, class: "label" %> 21 |
    22 | <%= f.text_field :name, class: "input", placeholder: "Name" %> 23 |
    24 |

    25 | The identifier of the field, the value must follow the pattern <%= Field::NAME_REGEX.source %> and Ruby's variable naming rule. 26 |

    27 |
    28 | 29 |
    30 | <%= f.label :label, class: "label" %> 31 |
    32 | <%= f.text_field :label, class: "input", placeholder: "Label" %> 33 |
    34 |
    35 | 36 |
    37 | <%= f.label :hint, class: "label" %> 38 |
    39 | <%= f.text_field :hint, class: "input", placeholder: "Hint" %> 40 |
    41 |
    42 | 43 |
    44 | <%= f.label :type, class: "label" %> 45 |
    46 | 47 | <%= f.select :type, options_for_field_types(selected: field.class.to_s) %> 48 | 49 |
    50 |
    51 | 52 |
    53 |
    54 | <%= f.submit class: "button is-primary" %> 55 |
    56 |
    57 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 58 |
    59 |
    60 | <% end %> 61 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing field

    4 | <%= render "form", field: @field %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New field", new_workflow_field_path(@workflow), class: "button is-primary" %> 5 |

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @fields.each do |field| %> 17 | 18 | 19 | 20 | 21 | 34 | 35 | <% end %> 36 | 37 |
    NameLabelType
    <%= field.name %><%= field.label %><%= field.class.model_name.human %> 22 | <% if field.validations_configurable? %> 23 | <%= link_to "Validations", edit_workflow_field_validations_path(@workflow, field) %> | 24 | <% end %> 25 | <% if field.options_configurable? %> 26 | <%= link_to "Options", edit_workflow_field_options_path(@workflow, field) %> | 27 | <% end %> 28 | <% if field.attached_nested_form? %> 29 | <%= link_to "Fields", workflow_nested_form_fields_url(@workflow, field.nested_form) %> | 30 | <% end %> 31 | <%= link_to "Edit", edit_workflow_field_path(@workflow, field) %> | 32 | <%= link_to "Destroy", workflow_field_path(@workflow, field), method: :delete, data: {confirm: "Are you sure?"} %> 33 |
    38 |
    39 |
    40 | 41 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New field

    4 | <%= render "form", field: @field %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/options/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: options, scope: :options, url: workflow_field_options_path(@workflow, field), method: :patch, local: true) do |f| %> 2 | <% if options.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(options.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 | <%= render "_form_core/field_options/#{field.type_key}", f: f, options: options %> 20 | 21 |
    22 |
    23 | <%= f.submit "Update", class: 'button is-primary' %> 24 |
    25 |
    26 | <%= link_to 'Back', url_for(:back), class: 'button is-link' %> 27 |
    28 |
    29 | <% end %> 30 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/options/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing options

    4 | <%= render "form", field: @field, options: @options %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/validations/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: validations, scope: :validations, url: workflow_field_validations_path(@workflow, field), method: :patch, local: true) do |f| %> 2 | <% if validations.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(validations.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 | <% validations.class.attribute_names.each do |attribute_name| %> 20 | <%= render "_form_core/validations/#{attribute_name}", f: f, validations: validations %> 21 | <% end %> 22 | <% validations.class.reflections.keys.each do |attribute_name| %> 23 | <%= render "_form_core/validations/#{attribute_name}", f: f, validations: validations %> 24 | <% end %> 25 | 26 |
    27 |
    28 | <%= f.submit "Update", class: "button is-primary" %> 29 |
    30 |
    31 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 32 |
    33 |
    34 | <% end %> 35 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/fields/validations/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing validations

    4 | <%= render "form", field: @field, validations: @validations %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New workflow", new_workflow_path, class: "button is-primary" %> 5 |

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @workflows.each do |workflow| %> 17 | 18 | 19 | 20 | 21 | 26 | 27 | <% end %> 28 | 29 |
    NameDescriptionUpdated at
    <%= workflow.name %><%= workflow.description %><%= workflow.updated_at %> 22 | <%= link_to "Show", workflow_path(workflow) %> | 23 | <%= link_to "Edit", edit_workflow_path(workflow) %> | 24 | <%= link_to "Destroy", workflow, method: :delete, data: {confirm: "Are you sure?"} %> 25 |
    30 |
    31 |
    32 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/instances/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New instance", new_workflow_instance_path(@workflow), class: "button is-primary" %> 5 |

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @instances.each do |instance| %> 17 | 18 | 19 | 20 | 21 | 24 | 25 | <% end %> 26 | 27 |
    IdStatusUpdated at
    <%= instance.id %><%= instance.status %><%= time_tag instance.updated_at %> 22 | <%= link_to "Show", workflow_instance_path(@workflow, instance) %> 23 |
    28 |
    29 |
    30 | 31 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/instances/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render "_form_core/render/form", 4 | form: @form, instance: @form_record, 5 | options: {scope: :form_record, url: workflow_instances_path(@workflow, @instance), local: true} %> 6 |
    7 |
    8 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/instances/show.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | <%= render "_form_core/preview/form", form: @form, instance: @form_record %> 5 |
    6 |
    7 |
    8 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/instances/tokens/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | <% @tokens.each do |token| %> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | <% end %> 38 | 39 |
    IdAssigneeStatusTransition idTransition nameCreated at
    19 | Instance status: <%= @instance.status %> 20 |
    <%= token.id %><%= token.assignable&.name %><%= token.status %><%= token.place&.output_transition&.id %><%= token.place&.output_transition&.name %><%= time_tag token.updated_at %> 32 | <% if token.processing? && token.place&.output_transition %> 33 | <%= link_to "Fire", workflow_instance_token_path(@workflow, @instance, token) %> 34 | <% end %> 35 |
    40 |
    41 |
    42 | 43 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/loads/show.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= form_with url: workflow_load_path(@workflow), method: :patch, local: true do |f| %> 4 |
    5 |
    6 | <%= f.text_area :bpmn_xml, class: "textarea", rows: 20 %> 7 |
    8 |
    9 | 10 |
    11 |
    12 | <%= f.file_field :bpmn_file %> 13 |
    14 |
    15 | 16 |
    17 |
    18 | <%= f.submit "Load from BPMN", class: "button is-primary" %> 19 |
    20 |
    21 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 22 |
    23 |
    24 | <% end %> 25 |
    26 |
    27 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/nested_forms/fields/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: [workflow, form, field.becomes(Field)], scope: :field, local: true) do |f| %> 2 | <% if field.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(field.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 |
    20 | <%= f.label :name, class: "label" %> 21 |
    22 | <%= f.text_field :name, class: "input", placeholder: "Name" %> 23 |
    24 |
    25 | 26 |
    27 | <%= f.label :label, class: "label" %> 28 |
    29 | <%= f.text_field :label, class: "input", placeholder: "Label" %> 30 |
    31 |
    32 | 33 |
    34 | <%= f.label :hint, class: "label" %> 35 |
    36 | <%= f.text_field :hint, class: "input", placeholder: "Hint" %> 37 |
    38 |
    39 | 40 |
    41 | <%= f.label :type, class: "label" %> 42 |
    43 | 44 | <%= f.select :type, options_for_field_types(selected: field.class.to_s) %> 45 | 46 |
    47 |
    48 | 49 |
    50 |
    51 | <% Field.accessibilities.each do |k, _| %> 52 | 56 | <% end %> 57 |
    58 |
    59 | 60 |
    61 |
    62 | <%= f.submit class: "button is-primary" %> 63 |
    64 |
    65 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 66 |
    67 |
    68 | <% end %> 69 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/nested_forms/fields/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing field

    4 | <%= render "form", workflow: @workflow, form: @nested_form, field: @field %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/nested_forms/fields/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | <%= link_to "New field", new_workflow_nested_form_field_path(@workflow, @nested_form), class: "button is-primary" %> 5 | <%= link_to "Back", smart_form_fields_path(@workflow, @nested_form.attachable.form), class: "button is-link" %> 6 |

    7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @fields.each do |field| %> 19 | 20 | 21 | 22 | 23 | 24 | 37 | 38 | <% end %> 39 | 40 |
    NameLabelTypeAccessibility
    <%= field.name %><%= field.label %><%= field.class.model_name.human %><%= Field.human_enum_value(:accessibility, field.accessibility) %> 25 | <% if field.validations_configurable? %> 26 | <%= link_to "Validations", edit_workflow_field_validations_path(@workflow, field) %> | 27 | <% end %> 28 | <% if field.options_configurable? %> 29 | <%= link_to "Options", edit_workflow_field_options_path(@workflow, field) %> | 30 | <% end %> 31 | <% if field.attached_nested_form? %> 32 | <%= link_to "Fields", workflow_nested_form_fields_url(@workflow, field.nested_form) %> | 33 | <% end %> 34 | <%= link_to "Edit", edit_workflow_nested_form_field_path(@workflow, @nested_form, field) %> | 35 | <%= link_to "Destroy", workflow_nested_form_field_path(@workflow, @nested_form, field), method: :delete, data: {confirm: "Are you sure?"} %> 36 |
    41 |
    42 |
    43 | 44 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/nested_forms/fields/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New field

    4 | <%= render "form", workflow: @workflow, form: @nested_form, field: @field %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New workflow

    4 | <%= render "form", workflow: @workflow %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/show.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 8 | 18 |
    19 |
    20 |
    21 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: [@workflow, transition.becomes(Transition)], scope: :transition, local: true) do |f| %> 2 | <% if transition.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(transition.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 |
    20 | <%= f.label :name, class: "label" %> 21 |
    22 | <%= f.text_field :name, class: "input", placeholder: "Name" %> 23 |
    24 |
    25 | 26 |
    27 | <%= f.label :type, class: "label" %> 28 |
    29 | 30 | <%= f.select :type, options_for_transition_types(selected: transition.class.to_s) %> 31 | 32 |
    33 |
    34 | 35 |
    36 |
    37 | <%= f.submit class: "button is-primary" %> 38 |
    39 |
    40 | <%= link_to "Back", url_for(:back), class: "button is-link" %> 41 |
    42 |
    43 | <% end %> 44 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing transition

    4 | <%= render "form", transition: @transition %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <% @transitions.each do |transition| %> 14 | 15 | 16 | 17 | 18 | 24 | 25 | <% end %> 26 | 27 |
    IdNameType
    <%= transition.id %><%= transition.name %><%= transition.class.model_name.human %> 19 | <% if transition.options_configurable? %> 20 | <%= link_to "Options", edit_workflow_transition_options_path(@workflow, transition) %> | 21 | <% end %> 22 | <%= link_to "Edit", edit_workflow_transition_path(@workflow, transition) %> 23 |
    28 |
    29 |
    30 | 31 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    New transition

    4 | <%= render "form", transition: @transition %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/options/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: options, scope: :options, url: workflow_transition_options_path(@workflow, transition), method: :patch, local: true) do |f| %> 2 | <% if options.errors.any? %> 3 |
    4 |
    5 |

    6 | <%= pluralize(options.errors.count, "error") %> prohibited this form from being saved: 7 |

    8 |
    9 |
    10 | 15 |
    16 |
    17 | <% end %> 18 | 19 | <%= render "_workflow_core/transition_options/#{transition.type_key}", f: f, transition: transition, options: options %> 20 | 21 |
    22 |
    23 | <%= f.submit "Update", class: 'button is-primary' %> 24 |
    25 |
    26 | <%= link_to 'Back', url_for(:back), class: 'button is-link' %> 27 |
    28 |
    29 | <% end %> 30 | -------------------------------------------------------------------------------- /test/dummy/app/views/workflows/transitions/options/edit.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Editing options

    4 | <%= render "form", transition: @transition, options: @options %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | APP_PATH = File.expand_path("../config/application", __dir__) 5 | require_relative "../config/boot" 6 | require "rails/commands" 7 | -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require_relative "../config/boot" 5 | require "rake" 6 | Rake.application.run 7 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "fileutils" 5 | include FileUtils 6 | 7 | # path to your application root. 8 | APP_ROOT = File.expand_path("..", __dir__) 9 | 10 | def system!(*args) 11 | system(*args) || abort("\n== Command #{args} failed ==") 12 | end 13 | 14 | chdir APP_ROOT do 15 | # This script is a starting point to setup your application. 16 | # Add necessary setup steps to this file. 17 | 18 | puts "== Installing dependencies ==" 19 | system! "gem install bundler --conservative" 20 | system("bundle check") || system!("bundle install") 21 | 22 | # Install JavaScript dependencies if using Yarn 23 | # system('bin/yarn') 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! "bin/rails db:setup" 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! "bin/rails log:clear tmp:clear" 35 | 36 | puts "\n== Restarting application server ==" 37 | system! "bin/rails restart" 38 | end 39 | -------------------------------------------------------------------------------- /test/dummy/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "fileutils" 5 | include FileUtils 6 | 7 | # path to your application root. 8 | APP_ROOT = File.expand_path("..", __dir__) 9 | 10 | def system!(*args) 11 | system(*args) || abort("\n== Command #{args} failed ==") 12 | end 13 | 14 | chdir APP_ROOT do 15 | # This script is a way to update your development environment automatically. 16 | # Add necessary update steps to this file. 17 | 18 | puts "== Installing dependencies ==" 19 | system! "gem install bundler --conservative" 20 | system("bundle check") || system!("bundle install") 21 | 22 | # Install JavaScript dependencies if using Yarn 23 | # system('bin/yarn') 24 | 25 | puts "\n== Updating database ==" 26 | system! "bin/rails db:migrate" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | APP_ROOT = File.expand_path("..", __dir__) 5 | Dir.chdir(APP_ROOT) do 6 | begin 7 | exec "yarnpkg", *ARGV 8 | rescue Errno::ENOENT 9 | $stderr.puts "Yarn executable was not detected in the system." 10 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 11 | exit 1 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require_relative "config/environment" 6 | 7 | run Rails.application 8 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "boot" 4 | 5 | require "rails/all" 6 | 7 | Bundler.require(*Rails.groups) 8 | require "workflow_core" 9 | 10 | Dir[Pathname.new(File.dirname(__FILE__)).realpath.parent.join("lib", "monkey_patches", "*.rb")].map do |file| 11 | require file 12 | end 13 | 14 | module Dummy 15 | class Application < Rails::Application 16 | # Initialize configuration defaults for originally generated Rails version. 17 | config.load_defaults 6.0 18 | 19 | # Settings in config/environments/* take precedence over those specified here. 20 | # Application configuration can go into files in config/initializers 21 | # -- all .rb files in that directory are automatically loaded after loading 22 | # the framework and any gems in your application. 23 | 24 | Rails.autoloaders.main.ignore(Rails.root.join("app", "overrides").to_s) 25 | 26 | config.to_prepare do 27 | Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| 28 | require_dependency(c) 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # tmp file for with_advisory_lock, see https://github.com/ClosureTree/with_advisory_lock/issues/3 4 | ENV["FLOCK_DIR"] ||= File.expand_path("../tmp/locks", __dir__) 5 | 6 | require "fileutils" 7 | FileUtils.mkdir_p ENV["FLOCK_DIR"] 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) 11 | 12 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 13 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__) 14 | -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative "application" 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # The test environment is used exclusively to run your application's 7 | # test suite. You never need to work with it otherwise. Remember that 8 | # your test database is "scratch space" for the test suite and is wiped 9 | # and recreated between test runs. Don't rely on the data there! 10 | config.cache_classes = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Store uploaded files on the local file system in a temporary directory 34 | config.active_storage.service = :test 35 | 36 | config.action_mailer.perform_caching = false 37 | 38 | # Tell Action Mailer not to deliver emails to the real world. 39 | # The :test delivery method accumulates sent emails in the 40 | # ActionMailer::Base.deliveries array. 41 | config.action_mailer.delivery_method = :test 42 | 43 | # Print deprecation notices to the stderr. 44 | config.active_support.deprecation = :stderr 45 | 46 | # Raises error for missing translations 47 | # config.action_view.raise_on_missing_translations = true 48 | end 49 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # ActiveSupport::Reloader.to_prepare do 6 | # ApplicationController.renderer.defaults.merge!( 7 | # http_host: 'example.org', 8 | # https: false 9 | # ) 10 | # end 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Version of your assets, change this if you want to expire all your assets. 6 | Rails.application.config.assets.version = "1.0" 7 | 8 | # Add additional assets to the asset load path. 9 | # Rails.application.config.assets.paths << Emoji.images_path 10 | # Add Yarn node_modules folder to the asset load path. 11 | Rails.application.config.assets.paths << Rails.root.join("node_modules") 12 | 13 | # Precompile additional assets. 14 | # application.js, application.css, and all non-JS/CSS in the app/assets 15 | # folder are already added. 16 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 17 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 6 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 7 | 8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 9 | # Rails.backtrace_cleaner.remove_silencers! 10 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Define an application-wide content security policy 6 | # For further information see the following documentation 7 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 8 | 9 | # Rails.application.config.content_security_policy do |policy| 10 | # policy.default_src :self, :https 11 | # policy.font_src :self, :https, :data 12 | # policy.img_src :self, :https, :data 13 | # policy.object_src :none 14 | # policy.script_src :self, :https 15 | # policy.style_src :self, :https 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Report CSP violations to a specified URI 25 | # For further information see the following documentation: 26 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 27 | # Rails.application.config.content_security_policy_report_only = true 28 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Specify a serializer for the signed and encrypted cookie jars. 6 | # Valid options are :json, :marshal, and :hybrid. 7 | Rails.application.config.action_dispatch.cookies_serializer = :json 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure sensitive parameters which will be filtered from the log file. 6 | Rails.application.config.filter_parameters += [:password] 7 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/form_core.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FormCore.virtual_model_class = ::VirtualModel 4 | FormCore.virtual_model_coder_class = FormCore::HashCoder 5 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new inflection rules using the following format. Inflections 6 | # are locale specific, and you may define rules for as many different 7 | # locales as you wish. All of these examples are active by default: 8 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 9 | # inflect.plural /^(ox)$/i, '\1en' 10 | # inflect.singular /^(ox)en/i, '\1' 11 | # inflect.irregular 'person', 'people' 12 | # inflect.uncountable %w( fish sheep ) 13 | # end 14 | 15 | # These inflection rules are supported but not enabled by default: 16 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 17 | # inflect.acronym 'RESTful' 18 | # end 19 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new mime types for use in respond_to blocks: 6 | # Mime::Type.register "text/richtext", :rtf 7 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] 11 | end 12 | 13 | # To enable root element in JSON for ActiveRecord objects. 14 | # ActiveSupport.on_load(:active_record) do 15 | # self.include_root_in_json = true 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | values: 34 | 'true': 'Yes' 35 | 'false': 'No' 36 | -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Puma can serve each request in a thread from an internal thread pool. 4 | # The `threads` method setting takes two numbers: a minimum and maximum. 5 | # Any libraries that use thread pools should be configured to match 6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 7 | # and maximum; this matches the default thread size of Active Record. 8 | # 9 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 10 | threads threads_count, threads_count 11 | 12 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 13 | # 14 | port ENV.fetch("PORT") { 3000 } 15 | 16 | # Specifies the `environment` that Puma will run in. 17 | # 18 | environment ENV.fetch("RAILS_ENV") { "development" } 19 | 20 | # Specifies the number of `workers` to boot in clustered mode. 21 | # Workers are forked webserver processes. If using threads and workers together 22 | # the concurrency of the application would be max `threads` * `workers`. 23 | # Workers do not work on JRuby or Windows (both of which do not support 24 | # processes). 25 | # 26 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 27 | 28 | # Use the `preload_app!` method when specifying a `workers` number. 29 | # This directive tells Puma to first boot the application and load code 30 | # before forking the application. This takes advantage of Copy On Write 31 | # process behavior so workers use less memory. 32 | # 33 | # preload_app! 34 | 35 | # Allow puma to be restarted by `rails restart` command. 36 | plugin :tmp_restart 37 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.routes.draw do 4 | resources :users, except: %i[show] 5 | resources :groups, except: %i[show] 6 | resource :sessions, only: %i[create destroy] 7 | 8 | resources :workflows do 9 | scope module: :workflows do 10 | resources :fields, except: %i[show] do 11 | scope module: :fields do 12 | resource :validations, only: %i[edit update] 13 | resource :options, only: %i[edit update] 14 | end 15 | end 16 | resources :nested_forms, only: %i[] do 17 | scope module: :nested_forms do 18 | resources :fields, except: %i[show] 19 | end 20 | end 21 | resources :transitions, except: %i[show] do 22 | scope module: :transitions do 23 | resource :options, only: %i[edit update] 24 | end 25 | end 26 | resources :instances, only: %i[index show new create] do 27 | scope module: :instances do 28 | resources :tokens, only: %i[index show] do 29 | post "fire" 30 | end 31 | end 32 | end 33 | 34 | resource :load, only: %i[show update] 35 | end 36 | end 37 | 38 | root to: "workflows#index" 39 | end 40 | -------------------------------------------------------------------------------- /test/dummy/config/spring.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | %w[ 4 | .ruby-version 5 | .rbenv-vars 6 | tmp/restart.txt 7 | tmp/caching-dev.txt 8 | ].each { |path| Spring.watch(path) } 9 | -------------------------------------------------------------------------------- /test/dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916202025_create_forms.form_core.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This migration comes from form_core (originally 20170430190404) 4 | class CreateForms < ActiveRecord::Migration[6.0] 5 | def change 6 | create_table :forms do |t| 7 | t.string :name, null: false, index: { unique: true } 8 | t.string :type, null: false, index: true 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916202026_create_fields.form_core.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This migration comes from form_core (originally 20170430191336) 4 | class CreateFields < ActiveRecord::Migration[6.0] 5 | def change 6 | create_table :fields do |t| 7 | t.string :name, null: false 8 | t.integer :accessibility, null: false 9 | t.text :validations 10 | t.text :options 11 | t.string :type, null: false, index: true 12 | t.references :form, foreign_key: true 13 | 14 | t.timestamps 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916202027_add_attachable_to_forms.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddAttachableToForms < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :forms do |t| 6 | t.references :attachable, polymorphic: true, index: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916202121_add_columns_to_fields.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToFields < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :fields do |t| 6 | t.string :label, default: "" 7 | t.string :hint, default: "" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916202124_add_position_to_fields.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddPositionToFields < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :fields do |t| 6 | t.integer :position 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180916215022_add_columns_to_workflows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToWorkflows < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflows do |t| 6 | t.references :form, foreign_key: true 7 | 8 | t.string :name, default: "" 9 | t.text :description, default: "" 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180917182517_create_groups.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateGroups < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :groups do |t| 6 | t.string :name 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180917184018_create_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateUsers < ActiveRecord::Migration[6.0] 4 | def change 5 | create_table :users do |t| 6 | t.string :name 7 | t.references :group, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180918202819_add_columns_to_transitions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToTransitions < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflow_transitions do |t| 6 | t.string :name 7 | t.string :uid 8 | t.text :options 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180918202827_add_columns_to_places.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToPlaces < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflow_places do |t| 6 | t.string :name 7 | t.string :uid 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180922095451_add_columns_to_workflow_instances.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToWorkflowInstances < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflow_instances do |t| 6 | t.string :type 7 | 8 | t.references :creator, polymorphic: true, index: true 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20180922095933_add_columns_to_workflow_tokens.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class AddColumnsToWorkflowTokens < ActiveRecord::Migration[6.0] 4 | def change 5 | change_table :workflow_tokens do |t| 6 | t.string :type 7 | 8 | t.text :payload 9 | 10 | t.references :assignable, polymorphic: true, index: true 11 | t.references :forwardable, polymorphic: true, index: true 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /test/dummy/lib/monkey_patches/active_support.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "active_support/concern+prependable" 4 | -------------------------------------------------------------------------------- /test/dummy/lib/monkey_patches/active_support/concern+prependable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Cheated from https://gitlab.com/gitlab-org/gitlab-ee/blob/master/config/initializers/0_as_concern.rb 4 | # This module is based on: https://gist.github.com/bcardarella/5735987 5 | 6 | module Prependable 7 | def prepend_features(base) 8 | if base.instance_variable_defined?(:@_dependencies) 9 | base.instance_variable_get(:@_dependencies) << self 10 | false 11 | else 12 | return false if base < self 13 | 14 | super 15 | base.singleton_class.send(:prepend, const_get("ClassMethods")) if const_defined?(:ClassMethods) 16 | @_dependencies.each { |dep| base.send(:prepend, dep) } 17 | base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) 18 | end 19 | end 20 | end 21 | 22 | module ActiveSupport 23 | module Concern 24 | prepend Prependable 25 | 26 | alias prepended included 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/dummy/lib/monkey_patches/big_decimal.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class BigDecimal 4 | # TODO: TEMP FIX BECAUSE SCRIPT_CORE NOT DONE YET. 5 | def to_msgpack(*args) 6 | to_f.to_msgpack(*args) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/log/.keep -------------------------------------------------------------------------------- /test/dummy/mruby/engine.gembox: -------------------------------------------------------------------------------- 1 | MRuby::GemBox.new do |conf| 2 | conf.gem(github: "jasl-lab/mruby-mpdecimal", branch: "master") 3 | 4 | conf.gem(core: "mruby-math") 5 | conf.gem(core: "mruby-time") 6 | conf.gem(core: "mruby-struct") 7 | conf.gem(core: "mruby-enum-ext") 8 | conf.gem(core: "mruby-string-ext") 9 | conf.gem(core: "mruby-numeric-ext") 10 | conf.gem(core: "mruby-array-ext") 11 | conf.gem(core: "mruby-hash-ext") 12 | conf.gem(core: "mruby-range-ext") 13 | conf.gem(core: "mruby-proc-ext") 14 | conf.gem(core: "mruby-symbol-ext") 15 | conf.gem(core: "mruby-object-ext") 16 | conf.gem(core: "mruby-toplevel-ext") 17 | conf.gem(core: "mruby-kernel-ext") 18 | conf.gem(core: "mruby-compiler") 19 | conf.gem(core: "mruby-metaprog") 20 | end 21 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/mruby/lib/.keep -------------------------------------------------------------------------------- /test/dummy/mruby/lib/core_ext.rb: -------------------------------------------------------------------------------- 1 | class FalseClass 2 | def dup 3 | self 4 | end 5 | 6 | def pack 7 | dup 8 | end 9 | end 10 | 11 | class TrueClass 12 | def dup 13 | self 14 | end 15 | 16 | def pack 17 | dup 18 | end 19 | end 20 | 21 | class Method 22 | def dup 23 | self 24 | end 25 | end 26 | 27 | class NilClass 28 | def dup 29 | self 30 | end 31 | 32 | def pack 33 | dup 34 | end 35 | end 36 | 37 | class String 38 | def pack 39 | dup 40 | end 41 | end 42 | 43 | class Numeric 44 | def dup 45 | self 46 | end 47 | 48 | def pack 49 | dup 50 | end 51 | end 52 | 53 | class Decimal 54 | def dup 55 | self 56 | end 57 | 58 | def pack 59 | to_s 60 | end 61 | end 62 | 63 | class Time 64 | class << self 65 | attr_accessor :formatted_offset 66 | end 67 | 68 | def pack 69 | "#{year}-#{month.to_s.rjust(2, "0")}-#{day.to_s.rjust(2, "0")} #{hour.to_s.rjust(2, "0")}:#{min.to_s.rjust(2, "0")}:#{sec.to_s.rjust(2, "0")} #{utc? ? "UTC" : Time.formatted_offset}".rstrip 70 | end 71 | end 72 | 73 | class Date < Time 74 | def pack 75 | "#{year}-#{month}-#{day}" 76 | end 77 | end 78 | 79 | class Symbol 80 | def dup 81 | self 82 | end 83 | 84 | def pack 85 | dup 86 | end 87 | end 88 | 89 | class Object 90 | def deep_dup 91 | dup 92 | end 93 | 94 | def pack 95 | raise NotImplementedError, "You need to implement #{self.class}#pack so that it can be output value" 96 | end 97 | end 98 | 99 | class Hash 100 | def deep_dup 101 | # Different with Shopify's 102 | each_with_object(dup) do |(key, value), hash| 103 | hash[key.deep_dup] = value.deep_dup 104 | end 105 | end 106 | 107 | def deep_pack 108 | each_with_object(dup) do |(key, value), hash| 109 | hash[key.pack] = value.pack 110 | end 111 | end 112 | alias pack deep_pack 113 | end 114 | 115 | class Array 116 | def deep_dup 117 | map(&:deep_dup) 118 | end 119 | 120 | def deep_pack 121 | map(&:pack) 122 | end 123 | alias pack deep_pack 124 | end 125 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/harden.rb: -------------------------------------------------------------------------------- 1 | Kernel.remove_method :object_id 2 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/input.rb: -------------------------------------------------------------------------------- 1 | module Input 2 | class << self 3 | def load(input) 4 | @input = input || {} 5 | @input.freeze 6 | end 7 | 8 | def value 9 | @input 10 | end 11 | 12 | def pack 13 | @input.pack 14 | end 15 | 16 | def [](key) 17 | @input[key] 18 | end 19 | 20 | def each(*args, &block) 21 | @input.each(*args, &block) 22 | end 23 | 24 | def map(*args, &block) 25 | @input.map(*args, &block) 26 | end 27 | 28 | def to_s 29 | @input.to_s 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/io.rb: -------------------------------------------------------------------------------- 1 | class << self 2 | attr_accessor :stdout_buffer 3 | end 4 | 5 | SCRIPT__TOP = self 6 | SCRIPT__TOP.stdout_buffer = "" 7 | 8 | module Kernel 9 | def puts(*args) 10 | if args.any? 11 | args.each do |arg| 12 | SCRIPT__TOP.stdout_buffer << "#{arg}\n" 13 | end 14 | else 15 | SCRIPT__TOP.stdout_buffer << "\n" 16 | end 17 | 18 | nil 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/output.rb: -------------------------------------------------------------------------------- 1 | module Output 2 | class << self 3 | attr_accessor :value 4 | 5 | def pack 6 | value.pack 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/dummy/mruby/lib/script_kernel.rb: -------------------------------------------------------------------------------- 1 | def prepare 2 | input = @input || {} 3 | remove_instance_variable "@input" 4 | 5 | configuration = input[:configuration] || {} 6 | Time.formatted_offset = configuration[:time_zone_offset] || 0 7 | 8 | Input.load input[:payload] 9 | end 10 | 11 | def pack_output 12 | instance_variable_set "@output", Output.pack 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dummy", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/dummy/storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/dummy/storage/.keep -------------------------------------------------------------------------------- /test/fixtures/workflow_core/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/fixtures/workflow_core/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/integration/.keep -------------------------------------------------------------------------------- /test/models/workflow_core/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails-engine/workflow_core/c374337bc094dace0e41a556f1191f424949f052/test/models/workflow_core/.keep -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Configure Rails Environment 4 | ENV["RAILS_ENV"] = "test" 5 | 6 | require_relative "../test/dummy/config/environment" 7 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)] 8 | ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__) 9 | require "rails/test_help" 10 | 11 | # Filter out Minitest backtrace while allowing backtrace from other libraries 12 | # to be shown. 13 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new 14 | 15 | # Load fixtures from the engine 16 | if ActiveSupport::TestCase.respond_to?(:fixture_path=) 17 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) 18 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path 19 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" 20 | ActiveSupport::TestCase.fixtures :all 21 | end 22 | -------------------------------------------------------------------------------- /test/workflow_core_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class WorkflowCore::Test < ActiveSupport::TestCase 6 | test "truth" do 7 | assert_kind_of Module, WorkflowCore 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /workflow_core.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("lib", __dir__) 4 | 5 | # Maintain your gem's version: 6 | require "workflow_core/version" 7 | 8 | # Describe your gem and declare its dependencies: 9 | Gem::Specification.new do |s| 10 | s.name = "workflow_core" 11 | s.version = WorkflowCore::VERSION 12 | s.authors = ["jasl"] 13 | s.email = ["jasl9187@hotmail.com"] 14 | s.homepage = "https://github.com/rails-engine/workflow_core" 15 | s.summary = "A Rails engine which providing essential infrastructure of workflow. It's based on Workflow Nets." 16 | s.description = "A Rails engine which providing essential infrastructure of workflow. It's based on Workflow Nets." 17 | s.license = "MIT" 18 | 19 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 20 | 21 | s.add_dependency "rails", ">= 6.0.0.rc1", "< 7.0.0" 22 | end 23 | --------------------------------------------------------------------------------