View on GitHub

Rails workflow

Small and flexible engine to build business processes in Rails applications.

Download this project as a .zip file Download this project as a tar.gz file

Quick Start

Starting / building process

ProcessManager is responsible for building and starting process.

  RailsWorkflow::ProcessManager.build_process template_id, context
  RailsWorkflow::ProcessManager.start_process template_id, context

template_id - is an id of your ProcessTemplate
context - is a hash, for example { resource: product }

Process

Main process responsibility is to create new operations when it is necessary.

process.build_dependencies operation

operation is the one which is just completed or changed it's state. Process checks operation templates from current process's templates and searching those who depends on given operation's template and it's current state.

If any operation templates are found, process checks if they can be created using resolve_dependency method described below operation is the one which was just completed. You can use operation.process, operation.context and any other application data to build your conditions. Check Operation Template for more info.

Process also has following methods which can be usefull to you:

  # checks if process can start. By default process can
  # start if it has operations.
  process.can_start?

  process.start

  # checks if process can be completed. If all
  # process synchronous operations is
  # completed - then process can complete.
  # Asynchronous operations may stay
  # incomplete (that's why they asynchronous)
  process.can_complete?

  # operations which is not yet completed. They may be
  # in_progress, waiting, error, etc.
  # Completed operations - done, skipped, canceled.
  process.incompleted_operations

  # works everytime when operation is notify process
  # that it's completed. By default this
  # method trying to build new operations and complete process.
  process.operation_complete operation

  process.complete

Operation Template

Operation Template is responsible for detecting if operation should be created (resolving dependencies) and for building operation.

  # operation (the one which was just completed or changed it's status)
  # is used to check if new operation can be
  # created. You can use operation.process,
  # operation.context or anything else in your
  # application to build complex conditions.
  operation_template.resolve_dependency operation

  # here you can add some additional logic while building operation.
  operation_template.build_operation

  # here you can customize context for new operation.
  # operation - is new operations which context will be build
  # dependencies - is array of other process operations.
  # By default it is array with only one element - old operation
  # which has been completed and caused new operation creation. But it
  # also possible to have here few already completed operations
  # so that new operation context can be based on their contexts merge.
  RailsWorkflow::OperationTemplate.build_context operation, dependencies

In most cases you will have to customise context (using build_context) and resolve_dependecy methods.

Operation

  # checks if operation can start. By default checks
  # if operation status = NOT_STARTED.
  operation.can_start?

  # checks if operation can start. if operation can't
  # be started (for example, user operations) -
  # then operation status is set to waiting.
  # Otherwize operation is starting.
  operation.start

  # this is main method for you as
  # this is the place to add your code. :)
  operation.execute

  # checks if operation is completed
  operation.completed?

  # by default checks if operation
  # has child process and
  # if there is child process - then checks
  # it's status.
  operation.can_complete?

  # method mainly for user operations. Here
  # you can put code you want to
  # be executed when user completed some operation.
  operation.on_complete

This is all you need to know to start process. In most cases you will need to add your own OperationTemplate with dependency_resolution (depends on your application logic) and building context for operation. Plus you will need to create your own Operation classes with execute method.

Each execute method runs in nested transaction. Best practice is to use sidekiq and execute operations in background.

User Operations

There is specific methods for user operations you should know:

  # checks if operation may be assigned to
  # given user. By default operation can be
  # assigned if it is not yet assigned to any
  # user. If you using UserByRoleOperation - then
  # it checks user and operation roles. If you
  # use UserByGroupOperation
  # then it checks user and operation group values.
  operation.can_be_assigned? user

  # checks if operation is assigned to given user.
  operation.assigned? user

  # restores initial operation assigned to role or group
  operation.cancel_assignment user

  # checks if operation can be assigned to user
  operation.can_be_assigned? user

  # assigns operation to user
  operation.assign user

You need to provide url_path and url_params in user operation context as engine will redirect user to that url when he picks up/start operation.

I will add User Operations specific tutorial shortly but you already can check tutorial as it describes process with user operation.