diff --git a/README.adoc b/README.adoc index 799add0be..9d72f08f0 100644 --- a/README.adoc +++ b/README.adoc @@ -494,6 +494,12 @@ The workflow library itself uses this feature to tweak the graphical representation of the workflow. See below. +### Defining workflow dynamically from JSON + +For an advance example please see +link:https://github.com/geekq/workflow/blob/develop/test/workflow_from_json_test.rb[workflow_from_json_test.rb]. + + ### Compose workflow definition with `include` In case you have very extensive workflow definition or would like to reuse @@ -525,6 +531,7 @@ Changelog * link:https://github.com/geekq/workflow/pull/227[#227] Allow event arguments to be taken into account when selecting the event * link:https://github.com/geekq/workflow/pull/232[#232] Add ability to include partial workflow definitions for composability +* link:https://github.com/geekq/workflow/pull/241[#241] Example for defining workflow dynamically from JSON === New in the version 3.0.0 diff --git a/test/workflow_from_json_test.rb b/test/workflow_from_json_test.rb new file mode 100644 index 000000000..07b443b61 --- /dev/null +++ b/test/workflow_from_json_test.rb @@ -0,0 +1,158 @@ +require File.join(File.dirname(__FILE__), 'test_helper') +require 'workflow' +class WorkflowFromJsonTest < Minitest::Test + + test '#241 define a workflow from JSON' do + c = Class.new + c.class_eval do + include Workflow + + attr_reader :events + + def record(event) + @events ||= [] + @events << event + end + + def ran?(event) + @events.include? event + end + + def clear_events + @events = [] + end + + def event_1(test) + # puts "running event_1 with arg '#{test}'" + record __method__ + end + + def before_transition(from, to, triggering_event, *args, **kwargs) + # puts "before_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def on_transition(from, to, triggering_event, *args, **kwargs) + # puts "on_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def after_transition(from, to, triggering_event, *args, **kwargs) + # puts "after_transition #{from} -> #{to}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def event_1_condition? + # puts "event_1_condition? -> true" + record __method__ + true + end + + def on_state_1_exit(new_state, event, *args) + # puts "on_state_1_exit(#{new_state}, #{event}, #{args})" + record __method__ + end + + def entering_state_2(prior_state, triggering_event, *args, **kwargs) + # puts "entering_state_2 #{prior_state}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def entering_state_3(prior_state, triggering_event, *args, **kwargs) + # puts "entering_state_3 #{prior_state}, #{triggering_event}, #{args}, #{kwargs}" + record __method__ + end + + def on_state_2_entry(new_state, event, *args) + # puts "on_state_2_entry(#{new_state}, #{event}, #{args})" + record __method__ + end + + end + + ran_normal = [] + hash = { + state_1: { + events: { + event_1: { + transition_to: :state_2, + if: :event_1_condition?, + meta: { e1: 1 } + }, + }, + meta: { a: 1 } + }, + state_2: { + events: { + event_2: { + transition_to: :state_3, + } + }, + on_entry: :entering_state_2, + meta: { a: 2 } + }, + state_3: { + on_entry: "entering_state_3", + } + } + + spec = Workflow::Specification.new do + + hash.each_pair do |state_name, state_def| + + state state_name, state_def + + on_entry {|prior_state, triggering_event| send state_def[:on_entry], prior_state, triggering_event } if state_def.include?(:on_entry) + on_exit { ran_normal << :on_exit } + + state_def[:events]&.each_pair do |event_name, event_def| + event event_name, event_def + end + + end + + before_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :before_transition + before_transition(from, to, triggering_event, *args, **kwargs) + end + + on_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :on_transition + on_transition(from, to, triggering_event, *args, **kwargs) + end + + after_transition do |from, to, triggering_event, *args, **kwargs| + ran_normal << :after_transition + after_transition(from, to, triggering_event, *args, **kwargs) + end + + end + + c.send :assign_workflow, spec + + o = c.new + + assert o.state_1?, "Should be state_1" + refute o.state_2?, "Should not be state_2" + + o.event_1! "hello" + [:event_1_condition?, :event_1, :entering_state_2, :before_transition, :on_transition, :after_transition].each do |event| + assert o.ran?(event), "Should have run event #{event}" + end + + refute o.state_1?, "Should not be state_1" + assert o.state_2?, "Should be state_2" + + o.clear_events + o.event_2! + refute o.ran?(:event_1), "Should not have run event_1" + [:entering_state_3, :before_transition, :on_transition, :after_transition].each do |event| + assert o.ran?(event), "Should have run event #{event}" + end + + assert ran_normal.include?(:before_transition), "Should have run before_transition proc" + assert ran_normal.include?(:on_transition), "Should have run on_transition proc" + assert ran_normal.include?(:after_transition), "Should have run after_transition proc" + assert ran_normal.include?(:on_exit), "Should have run on_exit proc" + end +end