Home

Awesome

flor

tests Gem Version

Flor is a "Ruby workflow engine", if that makes any sense.

use

As a workflow engine, flor takes as input process definitions and executes them. Those executions may in turn call pieces of Ruby code or external scripts that perform the actual work. Those pieces of code and scripts are called "taskers".

The classical way to use a language interpreter is to instantiate it as needed and let it die as the host execution ends. A workflow engine is more like a server, it may host multiple executions. And if the workflow engine stops, it may be restarted and pick the work back, when it was when it stopped.

Flor process definitions are written in the flor language, a programming language mostly inspired by Scheme and Ruby. As always with programming languages, readability is hoped for, for a workflow engine this is especially necessary since those business processes are the bread and butter of business users.

Using flor in your Ruby project requires you to clearly separate business process definitions from taskers. Since a flor instance may host multiple process executions based on one or more process definitions, many of the taskers may be reused from one definition to the other. For instance, if a "send-invoice-to-customer" tasker is created it might get used in the "process-retail-order" and the "process-big-distribution-order" processes.

design

quickstart

This quickstart sets up a flor unit tied to a SQLite database, resets the database, binds two taskers and then launches a flow execution involving the two taskers. Finally, it prints out the resulting workitem as the execution has just terminated.

require 'flor/unit'

#ENV['FLOR_DEBUG'] = 'dbg,sto,stdout' # full sql + flor debug output
#ENV['FLOR_DEBUG'] = 'dbg,stdout' # flor debug output
  # uncomment to see the flor activity

sto_uri = 'sqlite://flor_qs.db'
sto_uri = 'jdbc:sqlite://flor_qs.db' if RUBY_PLATFORM.match?(/java/)

flor = Flor::Unit.new(loader: Flor::HashLoader, sto_uri: sto_uri)
  # instantiate flor unit

flor.storage.delete_tables
flor.storage.migrate
  # blank slate database

class DemoTasker < Flor::BasicTasker
  def task(message)
    (attd['times'] || 1).times do
      message['payload']['log'] << "#{tasker}: #{task_name}"
    end
    reply
  end
end
flor.add_tasker(:alice, DemoTasker)
flor.add_tasker(:bob, DemoTasker)
  # a simple logging tasker implementation bound under
  # two different tasker names

flor.start
  # start the flor unit, so that it can process executions

exid = flor.launch(
  %q{
    sequence
      alice 'hello' times: 2
      bob 'world'
  },
  payload: { log: [ "started at #{Time.now}" ] })
    # launch a new execution, one that chains alice and bob work

#r = flor.wait(exid, 'terminated')
r = flor.wait(exid)
  # wait for the execution to terminate or to fail

p r['point']
  # "terminated" hopefully
p r['payload']['log']
  # [ "started at 2019-03-31 10:20:18 +0900",
  #   "alice: hello", "alice: hello",
  #   "bob: world" ]

This quickstart is at doc/quickstart0/, it's a minimal, one-file Ruby quickstart.

There is also doc/quickstart1/, a more complex example, that shows a flor setup, where taskers and flows are laid out in a flor directory tree.

documentation

See doc/.

related projects

blog posts and presentations

other Ruby projects about workflows

There are various other Ruby and Ruby on Rails projects about workflows and business processes, each with its own take on them.

There is a workflow engine category on Awesome Ruby.

If you want your engine/library to be added in this list, don't hesitate to ask me on via an issue or a pull request.

It's not limited to Ruby, but there is a wider list at meirwah/awesome-workflow-engines.

license

MIT, see LICENSE.txt