Packages
mozart
0.6.6
1.0.1
1.0.0
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Mozart is an Elixir based BPM platform. It provides a powerful and highly readable DSL for implementing executable business process models.
Current section
86 Versions
Jump to
Current section
86 Versions
Compare versions
5
files changed
+101
additions
-13
deletions
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/CharlesIrvineKC/mozart">>}]}. |
| 2 2 | {<<"name">>,<<"mozart">>}. |
| 3 | - {<<"version">>,<<"0.6.5">>}. |
| 3 | + {<<"version">>,<<"0.6.6">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<"Mozart is a BPM platform written in Elixir. It is currently in active development,\nbut it has sufficient functionality for the development of BPM POCs.">>}. |
| 6 6 | {<<"elixir">>,<<"~> 1.16">>}. |
| @@ -56,12 +56,12 @@ | |
| 56 56 | <<"lib/mozart/task/send.ex">>,<<"lib/mozart/task/timer.ex">>, |
| 57 57 | <<"lib/mozart/task/subprocess.ex">>,<<"lib/mozart/task/join.ex">>, |
| 58 58 | <<"lib/mozart/task/user.ex">>,<<"lib/mozart/task/service.ex">>, |
| 59 | - <<"lib/mozart/task/receive.ex">>,<<"lib/mozart/task/prototype.ex">>, |
| 60 | - <<"lib/mozart/task/parallel.ex">>,<<"lib/mozart/task/rule.ex">>, |
| 61 | - <<"lib/mozart/task/repeat.ex">>,<<"lib/mozart/task/case.ex">>, |
| 62 | - <<"lib/mozart/task/reroute.ex">>,<<"lib/mozart/process_engine.ex">>, |
| 63 | - <<"lib/mozart/application.ex">>,<<"lib/mozart/data">>, |
| 64 | - <<"lib/mozart/data/bpm_application.ex">>, |
| 59 | + <<"lib/mozart/task/receive.ex">>,<<"lib/mozart/task/conditional.ex">>, |
| 60 | + <<"lib/mozart/task/prototype.ex">>,<<"lib/mozart/task/parallel.ex">>, |
| 61 | + <<"lib/mozart/task/rule.ex">>,<<"lib/mozart/task/repeat.ex">>, |
| 62 | + <<"lib/mozart/task/case.ex">>,<<"lib/mozart/task/reroute.ex">>, |
| 63 | + <<"lib/mozart/process_engine.ex">>,<<"lib/mozart/application.ex">>, |
| 64 | + <<"lib/mozart/data">>,<<"lib/mozart/data/bpm_application.ex">>, |
| 65 65 | <<"lib/mozart/data/process_model.ex">>, |
| 66 66 | <<"lib/mozart/data/process_state.ex">>,<<"lib/mozart/event">>, |
| 67 67 | <<"lib/mozart/event/task_exit.ex">>,<<".formatter.exs">>,<<"mix.exs">>, |
| @@ -11,6 +11,7 @@ defmodule Mozart.BpmProcess do | |
| 11 11 | end |
| 12 12 | ``` |
| 13 13 | """ |
| 14 | + alias Mozart.Task.Conditional |
| 14 15 | alias Mozart.Task.Service |
| 15 16 | alias Mozart.Task.User |
| 16 17 | alias Mozart.Task.Subprocess |
| @@ -24,6 +25,7 @@ defmodule Mozart.BpmProcess do | |
| 24 25 | alias Mozart.Task.Prototype |
| 25 26 | alias Mozart.Task.Repeat |
| 26 27 | alias Mozart.Task.Reroute |
| 28 | + alias Mozart.Task.Conditional |
| 27 29 | |
| 28 30 | alias Mozart.Type.Choice |
| 29 31 | alias Mozart.Type.MultiChoice |
| @@ -282,6 +284,26 @@ defmodule Mozart.BpmProcess do | |
| 282 284 | end |
| 283 285 | end |
| 284 286 | |
| 287 | + defmacro conditional_task(name, options, do: tasks) do |
| 288 | + quote do |
| 289 | + options = unquote(options) |
| 290 | + condition = Keyword.get(options, :condition) |
| 291 | + module = Keyword.get(options, :module) || __MODULE__ |
| 292 | + c_task = %Conditional{name: unquote(name), condition: condition, module: module} |
| 293 | + insert_new_task(c_task) |
| 294 | + @capture_subtasks true |
| 295 | + unquote(tasks) |
| 296 | + @subtasks set_next_tasks(@subtasks) |
| 297 | + first = List.first(@subtasks) |
| 298 | + last = List.last(@subtasks) |
| 299 | + c_task = Map.put(c_task, :first, first.name) |> Map.put(:last, last.name) |
| 300 | + @tasks Enum.map(@tasks, fn t -> if t.name == unquote(name), do: c_task, else: t end) |
| 301 | + @subtask_sets [@subtasks | @subtask_sets] |
| 302 | + @subtasks [] |
| 303 | + @capture_subtasks false |
| 304 | + end |
| 305 | + end |
| 306 | + |
| 285 307 | @doc """ |
| 286 308 | Used to define one of many parallel execution paths within a parallel task. Arguments are a task name a block of tasks. |
| @@ -328,7 +328,7 @@ defmodule Mozart.ProcessEngine do | |
| 328 328 | complete_on_task_exit_event(task.subprocess_pid) |
| 329 329 | end |
| 330 330 | |
| 331 | - # Todo: Code exit repeat task |
| 331 | + # TODO: Code exit repeat task |
| 332 332 | |
| 333 333 | update_completed_task_state(state, task, event.next) |> execute_process() |
| 334 334 | end |
| @@ -384,13 +384,33 @@ defmodule Mozart.ProcessEngine do | |
| 384 384 | state = |
| 385 385 | Map.put(state, :open_tasks, Map.put(state.open_tasks, new_task.uid, new_task)) |
| 386 386 | |
| 387 | - if new_task.type == :repeat do |
| 388 | - trigger_repeat_execution(state, new_task) |
| 387 | + state = |
| 388 | + if new_task.type == :repeat do |
| 389 | + trigger_repeat_execution(state, new_task) |
| 390 | + else |
| 391 | + state |
| 392 | + end |
| 393 | + |
| 394 | + if new_task.type == :conditional do |
| 395 | + trigger_conditional_execution(state, new_task) |
| 389 396 | else |
| 390 397 | state |
| 391 398 | end |
| 392 399 | end |
| 393 400 | |
| 401 | + defp trigger_conditional_execution(state, new_task) do |
| 402 | + if apply(new_task.module, new_task.condition, [state.data]) do |
| 403 | + first_task = get_new_task_instance(new_task.first, state) |
| 404 | + Logger.info("New #{first_task.type} task instance [#{first_task.name}][#{first_task.uid}]") |
| 405 | + state = Map.put(state, :open_tasks, Map.put(state.open_tasks, first_task.uid, first_task)) |
| 406 | + do_new_task_side_effects(first_task.type, first_task, state) |
| 407 | + else |
| 408 | + new_task = Map.put(new_task, :complete, true) |
| 409 | + open_tasks = Map.put(state.open_tasks, new_task.uid, new_task) |
| 410 | + Map.put(state, :open_tasks, open_tasks) |
| 411 | + end |
| 412 | + end |
| 413 | + |
| 394 414 | defp trigger_repeat_execution(state, new_task) do |
| 395 415 | if apply(new_task.module, new_task.condition, [state.data]) do |
| 396 416 | first_task = get_new_task_instance(new_task.first, state) |
| @@ -503,19 +523,37 @@ defmodule Mozart.ProcessEngine do | |
| 503 523 | |> Map.put(:open_tasks, open_tasks) |
| 504 524 | |> Map.put(:completed_tasks, completed_tasks) |
| 505 525 | |> check_for_repeat_task_completion(task) |
| 526 | + |> check_for_conditional_task_completion(task) |
| 506 527 | end |
| 507 528 | |
| 508 529 | defp check_for_repeat_task_completion(state, task) do |
| 509 | - r_task = find_by_last_task(state, task.name) |
| 530 | + r_task = find_repeat_task_by_last_task(state, task.name) |
| 510 531 | if r_task, do: trigger_repeat_execution(state, r_task), else: state |
| 511 532 | end |
| 512 533 | |
| 513 | - defp find_by_last_task(state, task_name) do |
| 534 | + defp check_for_conditional_task_completion(state, task) do |
| 535 | + c_task = find_conditional_task_by_last_task(state, task.name) |
| 536 | + if c_task do |
| 537 | + c_task = Map.put(c_task, :complete, true) |
| 538 | + open_tasks = Map.put(state.open_tasks, c_task.uid, c_task) |
| 539 | + Map.put(state, :open_tasks, open_tasks) |
| 540 | + else |
| 541 | + state |
| 542 | + end |
| 543 | + end |
| 544 | + |
| 545 | + defp find_repeat_task_by_last_task(state, task_name) do |
| 514 546 | Enum.find_value(state.open_tasks, fn {_key, t} -> |
| 515 547 | if t.type == :repeat && t.last == task_name, do: t |
| 516 548 | end) |
| 517 549 | end |
| 518 550 | |
| 551 | + defp find_conditional_task_by_last_task(state, task_name) do |
| 552 | + Enum.find_value(state.open_tasks, fn {_key, t} -> |
| 553 | + if t.type == :conditional && t.last == task_name, do: t |
| 554 | + end) |
| 555 | + end |
| 556 | + |
| 519 557 | defp update_completed_task_state(state, task, next_task) do |
| 520 558 | state = update_for_completed_task(state, task) |
| 521 559 | if next_task, do: create_next_tasks(state, next_task, task.name), else: state |
| @@ -547,6 +585,11 @@ defmodule Mozart.ProcessEngine do | |
| 547 585 | update_completed_task_state(state, task, task.next) |> execute_process() |
| 548 586 | end |
| 549 587 | |
| 588 | + defp complete_conditional_task(state, task) do |
| 589 | + Logger.info("Complete conditional task [#{task.name}]") |
| 590 | + update_completed_task_state(state, task, task.next) |> execute_process() |
| 591 | + end |
| 592 | + |
| 550 593 | defp complete_service_task(state, task) do |
| 551 594 | Logger.info("Complete service task [#{task.name}[#{task.uid}]") |
| 552 595 | |
| @@ -729,6 +772,9 @@ defmodule Mozart.ProcessEngine do | |
| 729 772 | complete_able_task.type == :repeat -> |
| 730 773 | complete_repeat_task(state, complete_able_task) |
| 731 774 | |
| 775 | + complete_able_task.type == :conditional -> |
| 776 | + complete_conditional_task(state, complete_able_task) |
| 777 | + |
| 732 778 | complete_able_task.type == :reroute -> |
| 733 779 | complete_reroute_task(state, complete_able_task) |
| 734 780 | end |
| @@ -772,5 +818,6 @@ defmodule Mozart.ProcessEngine do | |
| 772 818 | defp complete_able(t) when t.type == :join, do: t.inputs == [] |
| 773 819 | defp complete_able(t) when t.type == :user, do: t.complete |
| 774 820 | defp complete_able(t) when t.type == :repeat, do: t.complete |
| 821 | + defp complete_able(t) when t.type == :conditional, do: t.complete |
| 775 822 | defp complete_able(t) when t.type == :prototype, do: true |
| 776 823 | end |
| @@ -0,0 +1,19 @@ | |
| 1 | + defmodule Mozart.Task.Conditional do |
| 2 | + @moduledoc false |
| 3 | + defstruct [ |
| 4 | + :name, |
| 5 | + :next, |
| 6 | + :uid, |
| 7 | + :model, |
| 8 | + :condition, |
| 9 | + :module, |
| 10 | + :first, |
| 11 | + :last, |
| 12 | + :start_time, |
| 13 | + :finish_time, |
| 14 | + :duration, |
| 15 | + :process_uid, |
| 16 | + complete: false, |
| 17 | + type: :conditional |
| 18 | + ] |
| 19 | + end |
| @@ -4,7 +4,7 @@ defmodule Mozart.MixProject do | |
| 4 4 | def project do |
| 5 5 | [ |
| 6 6 | app: :mozart, |
| 7 | - version: "0.6.5", |
| 7 | + version: "0.6.6", |
| 8 8 | elixir: "~> 1.16", |
| 9 9 | elixirc_paths: ["lib", "test/support"], |
| 10 10 | start_permanent: Mix.env() == :prod, |