Flor is a "Ruby workflow engine", if that makes any sense.
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.
- 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)
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.
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
- mantor/floristry - visualize and interact with flor through Rails facilities
- floraison/pollen - a set of flor hooks that emit over the http
- floraison/florist - a flor worklist implementation
- floraison/flack - a flor wrapping Rack app
- floraison/fugit - a time library for flor and rufus-scheduler
- floraison/raabro - the PEG library flor uses for its parsing needs
- flor workflow engine - on flor itself
- the flor language - on the flor workflow definition language itself
- reddit answer on workflow engines - an answer to a Reddit question on workflow engines, archived as a post
- Flor, hubristic interpreter - RubyKaigi 2017, Hiroshima - presentation
- flor design 0 - running a simple execution, what happens - blog post
- flor, branch to branch - q1 2017 - very dry deck
- flor 2017 - q1 2017 - very dry deck
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"
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 Gitter or via a pull request.
It's not limited to Ruby, but there is a wider list at meirwah/awesome-workflow-engines.
MIT, see LICENSE.txt