Skip to content

floraison/flor

Repository files navigation

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

  • Strives to propose a scheming interpreter for long running executions
  • Is written in Ruby, a rather straightforward language with at least two wonderful implementations (MRI and JRuby, which is enterprise-friendly)
  • Stores everything as JSON (if it breaks it's still readable)
  • Stores in any database supported by Sequel (the JSON goes in the "content" columns, along with some index columns)
  • Favours naive/simple implementations over smart ones
  • All in all should be easy to maintain (engine itself and executions running on top of it)

quickstart

This quickstart sets up a flor unit tied to a SQLite database, resets the databse, 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 layed out in a flor directory tree.

documentation

See doc/.

  • doc/procedures/ - the basic building blocks of the flor language
  • doc/glossary - words and their meaning in the flor context
  • doc/patterns - workflow patterns and their flor (tentative) implementations

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.

  • Dynflow - "Dynflow (DYNamic workFLOW) is a workflow engine written in Ruby"
  • rails_workflow - "Rails Workflow Engine allows you to organize your application business logic by joining user- and auto- operations in processes"
  • rails_engine/flow_core - "A multi purpose, extendable, Workflow-net-based workflow engine for Rails applications"
  • Trailblazer - "The Advanced Business Logic Framework"
  • Petri Flow - "Petri Net Workflow Engine for Ruby" (Rails)
  • Pallets - "Simple and reliable workflow engine, written in Ruby"
  • Gush - "Fast and distributed workflow runner using ActiveJob and Redis"
  • Rukawa - Hyper simple workflow engine powered by concurrent-ruby

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