flowengine-cli 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +16 -1
- data/README.md +675 -33
- data/Rakefile +11 -12
- data/examples/01_hello_world.rb +29 -0
- data/examples/02_yes_or_no.rb +31 -0
- data/examples/03_food_preferences.rb +52 -0
- data/examples/04_event_registration.rb +75 -0
- data/examples/05_job_application.rb +103 -0
- data/examples/06_health_assessment.rb +153 -0
- data/examples/07_loan_application.rb +187 -0
- data/examples/08_intake.rb +33 -0
- data/justfile +39 -2
- data/lib/flowengine/cli/commands/graph.rb +4 -0
- data/lib/flowengine/cli/commands/run.rb +22 -28
- data/lib/flowengine/cli/commands/validate_flow.rb +26 -0
- data/lib/flowengine/cli/commands/version.rb +3 -0
- data/lib/flowengine/cli/commands.rb +4 -0
- data/lib/flowengine/cli/flow_loader.rb +12 -0
- data/lib/flowengine/cli/renderer.rb +40 -0
- data/lib/flowengine/cli/ui_helper.rb +82 -0
- data/lib/flowengine/cli/version.rb +2 -1
- data/lib/flowengine/cli.rb +3 -0
- metadata +123 -2
data/Rakefile
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "timeout"
|
|
6
|
+
require "yard"
|
|
7
7
|
|
|
8
8
|
def shell(*args)
|
|
9
|
-
puts "running: #{args.join(
|
|
10
|
-
system(args.join(
|
|
9
|
+
puts "running: #{args.join(" ")}"
|
|
10
|
+
system(args.join(" "))
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
task :clean do
|
|
14
|
-
shell(
|
|
14
|
+
shell("rm -rf pkg/ tmp/ coverage/ doc/ ")
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
task gem: [:build] do
|
|
18
|
-
shell(
|
|
18
|
+
shell("gem install pkg/*")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
task permissions: [:clean] do
|
|
@@ -24,12 +24,11 @@ task permissions: [:clean] do
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
YARD::Rake::YardocTask.new(:doc) do |t|
|
|
27
|
-
t.files = %w
|
|
28
|
-
t.options.unshift(
|
|
29
|
-
t.after = -> { exec(
|
|
27
|
+
t.files = %w[lib/**/*.rb exe/*.rb - README.md LICENSE.txt CHANGELOG.md]
|
|
28
|
+
t.options.unshift("--title", '"FlowEngine CLI is the CLI for validating FlowEngine in the Terminal"')
|
|
29
|
+
t.after = -> { exec("open doc/index.html") } if RUBY_PLATFORM =~ /darwin/
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
task build: :permissions
|
|
34
33
|
|
|
35
34
|
RSpec::Core::RakeTask.new(:spec)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 1: Hello World
|
|
4
|
+
#
|
|
5
|
+
# The simplest possible flow — three linear steps, no branching.
|
|
6
|
+
# Demonstrates: text input, number input, display output.
|
|
7
|
+
#
|
|
8
|
+
# Run: bundle exec exe/flowengine-cli run examples/01_hello_world.rb
|
|
9
|
+
|
|
10
|
+
FlowEngine.define do
|
|
11
|
+
start :name
|
|
12
|
+
|
|
13
|
+
step :name do
|
|
14
|
+
type :text
|
|
15
|
+
question "What is your name?"
|
|
16
|
+
transition to: :age
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
step :age do
|
|
20
|
+
type :number
|
|
21
|
+
question "How old are you?"
|
|
22
|
+
transition to: :farewell
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
step :farewell do
|
|
26
|
+
type :display
|
|
27
|
+
question "Thanks for stopping by! Have a great day."
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 2: Yes or No
|
|
4
|
+
#
|
|
5
|
+
# A single boolean branch. Answering "yes" takes a detour;
|
|
6
|
+
# answering "no" goes straight to the end.
|
|
7
|
+
# Demonstrates: boolean step, equals rule, simple branching.
|
|
8
|
+
#
|
|
9
|
+
# Run: bundle exec exe/flowengine-cli run examples/02_yes_or_no.rb
|
|
10
|
+
|
|
11
|
+
FlowEngine.define do
|
|
12
|
+
start :has_pet
|
|
13
|
+
|
|
14
|
+
step :has_pet do
|
|
15
|
+
type :boolean
|
|
16
|
+
question "Do you have a pet?"
|
|
17
|
+
transition to: :pet_name, if_rule: equals(:has_pet, true)
|
|
18
|
+
transition to: :done
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
step :pet_name do
|
|
22
|
+
type :text
|
|
23
|
+
question "What is your pet's name?"
|
|
24
|
+
transition to: :done
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
step :done do
|
|
28
|
+
type :display
|
|
29
|
+
question "All done! Thanks for answering."
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 3: Food Preferences
|
|
4
|
+
#
|
|
5
|
+
# Multiple independent branches from a single multi_select step.
|
|
6
|
+
# Each selection may activate a different follow-up question.
|
|
7
|
+
# Demonstrates: multi_select, contains rule, multiple transitions.
|
|
8
|
+
#
|
|
9
|
+
# Run: bundle exec exe/flowengine-cli run examples/03_food_preferences.rb
|
|
10
|
+
|
|
11
|
+
FlowEngine.define do
|
|
12
|
+
start :diet
|
|
13
|
+
|
|
14
|
+
step :diet do
|
|
15
|
+
type :multi_select
|
|
16
|
+
question "Which food categories do you enjoy?"
|
|
17
|
+
options %w[Meat Seafood Vegetarian Vegan Dessert]
|
|
18
|
+
transition to: :meat_preference, if_rule: contains(:diet, "Meat")
|
|
19
|
+
transition to: :seafood_preference, if_rule: contains(:diet, "Seafood")
|
|
20
|
+
transition to: :dessert_preference, if_rule: contains(:diet, "Dessert")
|
|
21
|
+
transition to: :summary
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
step :meat_preference do
|
|
25
|
+
type :single_select
|
|
26
|
+
question "What is your favorite type of meat?"
|
|
27
|
+
options %w[Beef Chicken Pork Lamb]
|
|
28
|
+
transition to: :seafood_preference, if_rule: contains(:diet, "Seafood")
|
|
29
|
+
transition to: :dessert_preference, if_rule: contains(:diet, "Dessert")
|
|
30
|
+
transition to: :summary
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
step :seafood_preference do
|
|
34
|
+
type :single_select
|
|
35
|
+
question "What is your favorite type of seafood?"
|
|
36
|
+
options %w[Salmon Tuna Shrimp Lobster Crab]
|
|
37
|
+
transition to: :dessert_preference, if_rule: contains(:diet, "Dessert")
|
|
38
|
+
transition to: :summary
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
step :dessert_preference do
|
|
42
|
+
type :single_select
|
|
43
|
+
question "What is your favorite dessert?"
|
|
44
|
+
options %w[Cake IceCream Pie Cookies Fruit]
|
|
45
|
+
transition to: :summary
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
step :summary do
|
|
49
|
+
type :display
|
|
50
|
+
question "Thanks! We've noted your food preferences."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 4: Event Registration
|
|
4
|
+
#
|
|
5
|
+
# Two levels of conditional logic. The ticket type determines one
|
|
6
|
+
# branch, and within VIP there's a further branch based on
|
|
7
|
+
# whether the guest wants a plus-one.
|
|
8
|
+
# Demonstrates: single_select, boolean, equals rule, two-level branching.
|
|
9
|
+
#
|
|
10
|
+
# Run: bundle exec exe/flowengine-cli run examples/04_event_registration.rb
|
|
11
|
+
|
|
12
|
+
FlowEngine.define do
|
|
13
|
+
start :attendee_name
|
|
14
|
+
|
|
15
|
+
step :attendee_name do
|
|
16
|
+
type :text
|
|
17
|
+
question "What is your full name?"
|
|
18
|
+
transition to: :ticket_type
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
step :ticket_type do
|
|
22
|
+
type :single_select
|
|
23
|
+
question "Select your ticket type:"
|
|
24
|
+
options %w[General VIP Speaker]
|
|
25
|
+
transition to: :vip_options, if_rule: equals(:ticket_type, "VIP")
|
|
26
|
+
transition to: :talk_title, if_rule: equals(:ticket_type, "Speaker")
|
|
27
|
+
transition to: :dietary
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# --- VIP branch (level 1) ---
|
|
31
|
+
|
|
32
|
+
step :vip_options do
|
|
33
|
+
type :boolean
|
|
34
|
+
question "Would you like to bring a plus-one?"
|
|
35
|
+
transition to: :plus_one_name, if_rule: equals(:vip_options, true)
|
|
36
|
+
transition to: :dietary
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# VIP plus-one sub-branch (level 2)
|
|
40
|
+
step :plus_one_name do
|
|
41
|
+
type :text
|
|
42
|
+
question "What is your plus-one's name?"
|
|
43
|
+
transition to: :dietary
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# --- Speaker branch (level 1) ---
|
|
47
|
+
|
|
48
|
+
step :talk_title do
|
|
49
|
+
type :text
|
|
50
|
+
question "What is the title of your talk?"
|
|
51
|
+
transition to: :av_needs
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Speaker AV sub-branch (level 2)
|
|
55
|
+
step :av_needs do
|
|
56
|
+
type :multi_select
|
|
57
|
+
question "What A/V equipment do you need?"
|
|
58
|
+
options %w[Projector Microphone Whiteboard ScreenShare None]
|
|
59
|
+
transition to: :dietary
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# --- Common path ---
|
|
63
|
+
|
|
64
|
+
step :dietary do
|
|
65
|
+
type :single_select
|
|
66
|
+
question "Any dietary restrictions?"
|
|
67
|
+
options %w[None Vegetarian Vegan GlutenFree Halal Kosher]
|
|
68
|
+
transition to: :confirmation
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
step :confirmation do
|
|
72
|
+
type :display
|
|
73
|
+
question "You're registered! See you at the event."
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 5: Job Application Intake
|
|
4
|
+
#
|
|
5
|
+
# Two levels of branching with composed rules (all/any).
|
|
6
|
+
# The role determines the first branch; experience level and
|
|
7
|
+
# specific answers open deeper sub-branches.
|
|
8
|
+
# Demonstrates: all(), any(), greater_than, contains, two-level branching.
|
|
9
|
+
#
|
|
10
|
+
# Run: bundle exec exe/flowengine-cli run examples/05_job_application.rb
|
|
11
|
+
|
|
12
|
+
FlowEngine.define do
|
|
13
|
+
start :applicant_name
|
|
14
|
+
|
|
15
|
+
step :applicant_name do
|
|
16
|
+
type :text
|
|
17
|
+
question "What is your full name?"
|
|
18
|
+
transition to: :role
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
step :role do
|
|
22
|
+
type :single_select
|
|
23
|
+
question "Which role are you applying for?"
|
|
24
|
+
options %w[Engineering Design ProductManagement Sales]
|
|
25
|
+
transition to: :engineering_skills, if_rule: equals(:role, "Engineering")
|
|
26
|
+
transition to: :design_portfolio, if_rule: equals(:role, "Design")
|
|
27
|
+
transition to: :years_experience
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# --- Engineering branch (level 1) ---
|
|
31
|
+
|
|
32
|
+
step :engineering_skills do
|
|
33
|
+
type :multi_select
|
|
34
|
+
question "Select your primary skills:"
|
|
35
|
+
options %w[Ruby Python JavaScript Go Rust Java]
|
|
36
|
+
transition to: :years_experience
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# --- Design branch (level 1) ---
|
|
40
|
+
|
|
41
|
+
step :design_portfolio do
|
|
42
|
+
type :text
|
|
43
|
+
question "Please provide a link to your portfolio:"
|
|
44
|
+
transition to: :years_experience
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# --- Common: experience (feeds level 2 branching) ---
|
|
48
|
+
|
|
49
|
+
step :years_experience do
|
|
50
|
+
type :number
|
|
51
|
+
question "How many years of professional experience do you have?"
|
|
52
|
+
transition to: :leadership_experience, if_rule: greater_than(:years_experience, 7)
|
|
53
|
+
transition to: :education
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Level 2 branch: senior applicants
|
|
57
|
+
step :leadership_experience do
|
|
58
|
+
type :boolean
|
|
59
|
+
question "Have you managed a team of 5 or more people?"
|
|
60
|
+
transition to: :management_style, if_rule: equals(:leadership_experience, true)
|
|
61
|
+
transition to: :education
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Level 2 sub-branch: managers
|
|
65
|
+
step :management_style do
|
|
66
|
+
type :single_select
|
|
67
|
+
question "How would you describe your management style?"
|
|
68
|
+
options %w[Collaborative Directive Coaching Delegative]
|
|
69
|
+
transition to: :education
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
step :education do
|
|
73
|
+
type :single_select
|
|
74
|
+
question "What is your highest level of education?"
|
|
75
|
+
options %w[HighSchool Bachelors Masters PhD Bootcamp SelfTaught]
|
|
76
|
+
transition to: :availability
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
step :availability do
|
|
80
|
+
type :single_select
|
|
81
|
+
question "When can you start?"
|
|
82
|
+
options %w[Immediately TwoWeeks OneMonth ThreeMonths]
|
|
83
|
+
transition to: :referral
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
step :referral do
|
|
87
|
+
type :boolean
|
|
88
|
+
question "Were you referred by a current employee?"
|
|
89
|
+
transition to: :referral_name, if_rule: equals(:referral, true)
|
|
90
|
+
transition to: :thanks
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
step :referral_name do
|
|
94
|
+
type :text
|
|
95
|
+
question "Who referred you?"
|
|
96
|
+
transition to: :thanks
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
step :thanks do
|
|
100
|
+
type :display
|
|
101
|
+
question "Application submitted! We'll be in touch within 5 business days."
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 6: Health Risk Assessment
|
|
4
|
+
#
|
|
5
|
+
# Three levels of conditional branching with composed rules.
|
|
6
|
+
#
|
|
7
|
+
# Level 1: Lifestyle choices branch into exercise, smoking, or diet paths.
|
|
8
|
+
# Level 2: Within exercise, intensity level opens a sub-branch.
|
|
9
|
+
# Within smoking, pack count opens a sub-branch.
|
|
10
|
+
# Level 3: Heavy smokers with additional risk factors get a deeper follow-up.
|
|
11
|
+
#
|
|
12
|
+
# Demonstrates: all(), any(), greater_than, less_than, contains, equals,
|
|
13
|
+
# three-level deep conditional logic.
|
|
14
|
+
#
|
|
15
|
+
# Run: bundle exec exe/flowengine-cli run examples/06_health_assessment.rb
|
|
16
|
+
|
|
17
|
+
FlowEngine.define do
|
|
18
|
+
start :patient_info
|
|
19
|
+
|
|
20
|
+
step :patient_info do
|
|
21
|
+
type :text
|
|
22
|
+
question "Patient name and date of birth (e.g., Jane Doe, 1985-03-15):"
|
|
23
|
+
transition to: :lifestyle
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
step :lifestyle do
|
|
27
|
+
type :multi_select
|
|
28
|
+
question "Select all that apply to your lifestyle:"
|
|
29
|
+
options %w[Exercise Smoking Alcohol HighStress PoorDiet]
|
|
30
|
+
transition to: :exercise_frequency, if_rule: contains(:lifestyle, "Exercise")
|
|
31
|
+
transition to: :smoking_details, if_rule: contains(:lifestyle, "Smoking")
|
|
32
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
33
|
+
transition to: :sleep_quality
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# ============================================================
|
|
37
|
+
# LEVEL 1 — Exercise branch
|
|
38
|
+
# ============================================================
|
|
39
|
+
|
|
40
|
+
step :exercise_frequency do
|
|
41
|
+
type :number
|
|
42
|
+
question "How many days per week do you exercise?"
|
|
43
|
+
transition to: :exercise_intensity, if_rule: greater_than(:exercise_frequency, 3)
|
|
44
|
+
transition to: :smoking_details, if_rule: contains(:lifestyle, "Smoking")
|
|
45
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
46
|
+
transition to: :sleep_quality
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# LEVEL 2 — Intense exerciser sub-branch
|
|
50
|
+
step :exercise_intensity do
|
|
51
|
+
type :single_select
|
|
52
|
+
question "What best describes your typical workout intensity?"
|
|
53
|
+
options %w[Moderate Vigorous Extreme]
|
|
54
|
+
transition to: :injury_history, if_rule: equals(:exercise_intensity, "Extreme")
|
|
55
|
+
transition to: :smoking_details, if_rule: contains(:lifestyle, "Smoking")
|
|
56
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
57
|
+
transition to: :sleep_quality
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# LEVEL 3 — Extreme exerciser injury check
|
|
61
|
+
step :injury_history do
|
|
62
|
+
type :boolean
|
|
63
|
+
question "Have you had any exercise-related injuries in the past year?"
|
|
64
|
+
transition to: :smoking_details, if_rule: contains(:lifestyle, "Smoking")
|
|
65
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
66
|
+
transition to: :sleep_quality
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# ============================================================
|
|
70
|
+
# LEVEL 1 — Smoking branch
|
|
71
|
+
# ============================================================
|
|
72
|
+
|
|
73
|
+
step :smoking_details do
|
|
74
|
+
type :number
|
|
75
|
+
question "How many packs per day do you smoke?"
|
|
76
|
+
transition to: :smoking_duration, if_rule: greater_than(:smoking_details, 1)
|
|
77
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
78
|
+
transition to: :sleep_quality
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# LEVEL 2 — Heavy smoker sub-branch
|
|
82
|
+
step :smoking_duration do
|
|
83
|
+
type :number
|
|
84
|
+
question "How many years have you been smoking?"
|
|
85
|
+
transition to: :smoking_cessation, if_rule: all(
|
|
86
|
+
greater_than(:smoking_duration, 10),
|
|
87
|
+
greater_than(:smoking_details, 1)
|
|
88
|
+
)
|
|
89
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
90
|
+
transition to: :sleep_quality
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# LEVEL 3 — Long-term heavy smoker: cessation counseling
|
|
94
|
+
step :smoking_cessation do
|
|
95
|
+
type :boolean
|
|
96
|
+
question "Have you tried a cessation program before?"
|
|
97
|
+
transition to: :cessation_details, if_rule: equals(:smoking_cessation, true)
|
|
98
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
99
|
+
transition to: :sleep_quality
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
step :cessation_details do
|
|
103
|
+
type :text
|
|
104
|
+
question "Please describe what programs you tried and when:"
|
|
105
|
+
transition to: :diet_details, if_rule: contains(:lifestyle, "PoorDiet")
|
|
106
|
+
transition to: :sleep_quality
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# ============================================================
|
|
110
|
+
# LEVEL 1 — Diet branch
|
|
111
|
+
# ============================================================
|
|
112
|
+
|
|
113
|
+
step :diet_details do
|
|
114
|
+
type :multi_select
|
|
115
|
+
question "Which of these describe your typical diet?"
|
|
116
|
+
options %w[HighSugar HighSodium LowFiber SkipsMeals FastFood ProcessedFoods]
|
|
117
|
+
transition to: :sleep_quality
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# ============================================================
|
|
121
|
+
# Common tail
|
|
122
|
+
# ============================================================
|
|
123
|
+
|
|
124
|
+
step :sleep_quality do
|
|
125
|
+
type :single_select
|
|
126
|
+
question "How would you rate your sleep quality?"
|
|
127
|
+
options %w[Excellent Good Fair Poor]
|
|
128
|
+
transition to: :mental_health, if_rule: any(
|
|
129
|
+
equals(:sleep_quality, "Fair"),
|
|
130
|
+
equals(:sleep_quality, "Poor")
|
|
131
|
+
)
|
|
132
|
+
transition to: :family_history
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
step :mental_health do
|
|
136
|
+
type :multi_select
|
|
137
|
+
question "Do any of these apply to you?"
|
|
138
|
+
options %w[Anxiety Depression Insomnia ChronicFatigue None]
|
|
139
|
+
transition to: :family_history
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
step :family_history do
|
|
143
|
+
type :multi_select
|
|
144
|
+
question "Select any conditions that run in your family:"
|
|
145
|
+
options %w[HeartDisease Diabetes Cancer Hypertension None]
|
|
146
|
+
transition to: :risk_summary
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
step :risk_summary do
|
|
150
|
+
type :display
|
|
151
|
+
question "Assessment complete. Your responses have been recorded for review by your provider."
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Example 7: Loan Application
|
|
4
|
+
#
|
|
5
|
+
# Three levels of conditional branching with composed rules (all, any).
|
|
6
|
+
#
|
|
7
|
+
# Level 1: Loan type (Personal / Mortgage / Business) splits into different paths.
|
|
8
|
+
# Level 2: Within Mortgage, property type triggers sub-branches.
|
|
9
|
+
# Within Business, revenue level triggers sub-branches.
|
|
10
|
+
# Level 3: High-value mortgage applicants with specific conditions get
|
|
11
|
+
# additional scrutiny. Low-revenue startups get a different path
|
|
12
|
+
# than established businesses.
|
|
13
|
+
#
|
|
14
|
+
# Demonstrates: all(), any(), greater_than, less_than, equals, contains,
|
|
15
|
+
# three-level deep conditional logic, number_matrix.
|
|
16
|
+
#
|
|
17
|
+
# Run: bundle exec exe/flowengine-cli run examples/07_loan_application.rb
|
|
18
|
+
|
|
19
|
+
FlowEngine.define do
|
|
20
|
+
start :applicant_info
|
|
21
|
+
|
|
22
|
+
step :applicant_info do
|
|
23
|
+
type :text
|
|
24
|
+
question "Full legal name:"
|
|
25
|
+
transition to: :loan_type
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
step :loan_type do
|
|
29
|
+
type :single_select
|
|
30
|
+
question "What type of loan are you applying for?"
|
|
31
|
+
options %w[Personal Mortgage Business]
|
|
32
|
+
transition to: :personal_amount, if_rule: equals(:loan_type, "Personal")
|
|
33
|
+
transition to: :mortgage_property, if_rule: equals(:loan_type, "Mortgage")
|
|
34
|
+
transition to: :business_info, if_rule: equals(:loan_type, "Business")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ============================================================
|
|
38
|
+
# LEVEL 1 — Personal loan (simple path)
|
|
39
|
+
# ============================================================
|
|
40
|
+
|
|
41
|
+
step :personal_amount do
|
|
42
|
+
type :number
|
|
43
|
+
question "How much would you like to borrow (in dollars)?"
|
|
44
|
+
transition to: :personal_purpose
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
step :personal_purpose do
|
|
48
|
+
type :single_select
|
|
49
|
+
question "What is the primary purpose of this loan?"
|
|
50
|
+
options %w[DebtConsolidation HomeImprovement Medical Travel Education Other]
|
|
51
|
+
transition to: :credit_check
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# ============================================================
|
|
55
|
+
# LEVEL 1 — Mortgage branch
|
|
56
|
+
# ============================================================
|
|
57
|
+
|
|
58
|
+
step :mortgage_property do
|
|
59
|
+
type :single_select
|
|
60
|
+
question "What type of property is this for?"
|
|
61
|
+
options %w[SingleFamily Condo Townhouse MultiFamily Commercial]
|
|
62
|
+
transition to: :mortgage_amount
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
step :mortgage_amount do
|
|
66
|
+
type :number
|
|
67
|
+
question "What is the estimated property value (in dollars)?"
|
|
68
|
+
transition to: :mortgage_high_value, if_rule: greater_than(:mortgage_amount, 750_000)
|
|
69
|
+
transition to: :down_payment
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# LEVEL 2 — High-value mortgage
|
|
73
|
+
step :mortgage_high_value do
|
|
74
|
+
type :boolean
|
|
75
|
+
question "Will this be your primary residence?"
|
|
76
|
+
transition to: :jumbo_review, if_rule: all(
|
|
77
|
+
equals(:mortgage_high_value, false),
|
|
78
|
+
greater_than(:mortgage_amount, 750_000)
|
|
79
|
+
)
|
|
80
|
+
transition to: :down_payment
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# LEVEL 3 — Investment property jumbo loan
|
|
84
|
+
step :jumbo_review do
|
|
85
|
+
type :number_matrix
|
|
86
|
+
question "Provide details about your existing properties:"
|
|
87
|
+
fields %w[OwnedProperties RentalProperties MortgagesOwed]
|
|
88
|
+
transition to: :down_payment
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
step :down_payment do
|
|
92
|
+
type :number
|
|
93
|
+
question "How much is your down payment (in dollars)?"
|
|
94
|
+
transition to: :credit_check
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# ============================================================
|
|
98
|
+
# LEVEL 1 — Business loan branch
|
|
99
|
+
# ============================================================
|
|
100
|
+
|
|
101
|
+
step :business_info do
|
|
102
|
+
type :text
|
|
103
|
+
question "Business name and EIN:"
|
|
104
|
+
transition to: :business_type
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
step :business_type do
|
|
108
|
+
type :single_select
|
|
109
|
+
question "Business structure:"
|
|
110
|
+
options %w[SoleProprietor LLC SCorp CCorp Partnership]
|
|
111
|
+
transition to: :annual_revenue
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
step :annual_revenue do
|
|
115
|
+
type :number
|
|
116
|
+
question "What is your annual revenue (in dollars)?"
|
|
117
|
+
transition to: :startup_details, if_rule: less_than(:annual_revenue, 100_000)
|
|
118
|
+
transition to: :established_details, if_rule: greater_than(:annual_revenue, 500_000)
|
|
119
|
+
transition to: :business_loan_amount
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# LEVEL 2 — Startup path
|
|
123
|
+
step :startup_details do
|
|
124
|
+
type :number
|
|
125
|
+
question "How many months has the business been operating?"
|
|
126
|
+
transition to: :startup_funding, if_rule: less_than(:startup_details, 12)
|
|
127
|
+
transition to: :business_loan_amount
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# LEVEL 3 — Very new startup
|
|
131
|
+
step :startup_funding do
|
|
132
|
+
type :multi_select
|
|
133
|
+
question "What funding sources have you used so far?"
|
|
134
|
+
options %w[PersonalSavings FriendsFamily AngelInvestor VentureCapital Grant CreditCards None]
|
|
135
|
+
transition to: :business_loan_amount
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# LEVEL 2 — Established business path
|
|
139
|
+
step :established_details do
|
|
140
|
+
type :number_matrix
|
|
141
|
+
question "Provide financial details:"
|
|
142
|
+
fields %w[Employees AnnualExpenses OutstandingDebt]
|
|
143
|
+
transition to: :established_expansion, if_rule: all(
|
|
144
|
+
greater_than(:annual_revenue, 500_000),
|
|
145
|
+
any(
|
|
146
|
+
equals(:business_type, "CCorp"),
|
|
147
|
+
equals(:business_type, "SCorp")
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
transition to: :business_loan_amount
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# LEVEL 3 — Corp expansion review
|
|
154
|
+
step :established_expansion do
|
|
155
|
+
type :multi_select
|
|
156
|
+
question "What will the loan fund?"
|
|
157
|
+
options %w[Hiring Equipment RealEstate Acquisition Marketing RAndD]
|
|
158
|
+
transition to: :business_loan_amount
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
step :business_loan_amount do
|
|
162
|
+
type :number
|
|
163
|
+
question "How much funding are you requesting (in dollars)?"
|
|
164
|
+
transition to: :credit_check
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# ============================================================
|
|
168
|
+
# Common tail
|
|
169
|
+
# ============================================================
|
|
170
|
+
|
|
171
|
+
step :credit_check do
|
|
172
|
+
type :boolean
|
|
173
|
+
question "Do you authorize a credit check?"
|
|
174
|
+
transition to: :review, if_rule: equals(:credit_check, true)
|
|
175
|
+
transition to: :declined
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
step :declined do
|
|
179
|
+
type :display
|
|
180
|
+
question "A credit check is required to proceed. Your application has been saved as a draft."
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
step :review do
|
|
184
|
+
type :display
|
|
185
|
+
question "Application submitted! You will receive a decision within 3-5 business days."
|
|
186
|
+
end
|
|
187
|
+
end
|